home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 April: Mac OS SDK / Dev.CD Apr 96 SDK / Dev.CD Apr 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc / Sample Code / Sample Editors⁄Viewers / Sound Editor / Source / SoundEditor.cpp < prev    next >
Encoding:
Text File  |  1995-12-11  |  176.1 KB  |  5,647 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2.  
  3.     File:            SoundEditor.cpp
  4.     
  5.     Written by:        Steve Smith, Andrey Dolgachev, and Troy Gaul
  6.     
  7.     Copyright:        © 1995 by Apple Computer, Inc., all rights reserved.
  8.     
  9.     Description:    The Sound Editor is a simple record/playback part using the
  10.                     Macintosh Sound Mgr. The part demonstrates handling of
  11.                     simple content, support for multiple content "kinds",
  12.                     reading/writing data from the Clipboard, use of library
  13.                     resources, and a movable process modal dialog. The part is
  14.                     based on SamplePart which should make it easier to
  15.                     understand.
  16.     
  17.     Demonstrates:    The following recipes were used in implementing the
  18.                     SoundEditor:
  19.                      1. Open
  20.                      2. Adding A Display Frame
  21.                      3. Adding A Facet
  22.                      4. Part Drawing
  23.                      5. Refcounting Geometry
  24.                      6. ViewTypes & Presentations
  25.                      7. Display Frame 'ternalization
  26.                      8. Part Init & Externalization
  27.                      9. Part Init & partWrapper
  28.                     10. Part Storage Model
  29.                     11. Persistent Reference
  30.                     12. RefCounting
  31.                     13. Storage Unit
  32.                     14. Activation
  33.                     15. Basic Event Handling
  34.                     16. Menus
  35.                     17. Windows & Dialogs
  36.                     18. Exception Handling
  37.                     19. Memory Manager
  38.                     20. Using Resources
  39.                     21. Clipboard Part 1 & 2
  40.                     22. Dropping Non-OpenDoc Files
  41.                     23. Multiple Kind Support
  42.     
  43.     
  44.     Notes:            • For methods where it is necessary to call the parent class
  45.                     implementation, the calls are made from the SOM class. See
  46.                     som_SamplePart.cpp and the Class Reference to know whether
  47.                     you need to call the parent class from any method.
  48.                     
  49.             
  50. ------------------------------------------------------------------------------*/
  51.  
  52. // -- Compiler/Preprocessor Switches --
  53.  
  54. #ifndef _COMPILERDEFS_
  55. #include "CompDefs.h"
  56. #endif
  57.  
  58. // -- OpenDoc Utilities --
  59.  
  60. #ifndef _EXCEPT_
  61. // Exceptions define several important macros (eg. CHECKENV)
  62. // which are used in the SOM method dispatch glue. If Except.h
  63. // is not included early enough, exceptions may not be thrown
  64. // correctly when returning from a SOM method with the "ev" parameter set.
  65. #include <Except.h>
  66. #endif
  67.  
  68. // -- SoundEditor Includes --
  69.  
  70. #ifndef _SOUNDEDITOR_
  71. #include "SoundEditor.h"
  72. #endif
  73.  
  74. #ifndef _SOUNDEDITORDEF_
  75. #include "SoundEditorDef.h"
  76. #endif
  77.  
  78. #ifndef _SOUNDEDITORGLOBALS_
  79. #include "SoundEditorGlobals.h"
  80. #endif
  81.  
  82. #ifndef _SOUNDEDITORUTILS_
  83. #include "SoundEditorUtils.h"
  84. #endif
  85.  
  86. #ifndef _SOUNDHANDLING_
  87. #include "SoundHandling.h"
  88. #endif
  89.  
  90. #ifndef SOM_som_SESettingsExtension_xh
  91. #include "som_SESettingsExtension.xh"
  92. #endif
  93.  
  94. #ifndef SOM_SoundEditorSI_xih
  95. #include "som_SoundEditorSI.xh"
  96. #endif
  97.  
  98. #ifndef _SAMPLECOLLECTIONS_
  99. #include "SampleCollections.h"
  100. #endif
  101.  
  102. // -- OpenDoc Includes --
  103.  
  104. #ifndef SOM_ODArbitrator_xh
  105. #include <Arbitrat.xh>
  106. #endif
  107.  
  108. #ifndef SOM_ODCanvas_xh
  109. #include <Canvas.xh>
  110. #endif
  111.  
  112. #ifndef SOM_ODClipboard_xh
  113. #include <Clipbd.xh>
  114. #endif
  115.  
  116. #ifndef SOM_Module_OpenDoc_Commands_defined
  117. #include <CmdDefs.xh>
  118. #endif
  119.  
  120. #ifndef SOM_ODDispatcher_xh
  121. #include <Disptch.xh>
  122. #endif
  123.  
  124. #ifndef SOM_ODDraft_xh
  125. #include <Draft.xh>
  126. #endif
  127.  
  128. #ifndef SOM_ODFacet_xh
  129. #include <Facet.xh>
  130. #endif
  131.  
  132. #ifndef SOM_Module_OpenDoc_Foci_defined
  133. #include <Foci.xh>
  134. #endif
  135.  
  136. #ifndef SOM_ODFocusSet_xh
  137. #include <FocusSet.xh>
  138. #endif
  139.  
  140. #ifndef SOM_ODFrame_xh
  141. #include <Frame.xh>
  142. #endif
  143.  
  144. #ifndef SOM_ODMenuBar_xh
  145. #include <MenuBar.xh>
  146. #endif
  147.  
  148. #ifndef SOM_ODSession_xh
  149. #include <ODSessn.xh>
  150. #endif
  151.  
  152. #ifndef _ODTYPES_
  153. #include <ODTypes.h>
  154. #endif
  155.  
  156. #ifndef SOM_ODPart_xh
  157. #include <Part.xh>
  158. #endif
  159.  
  160. #ifndef SOM_ODShape_xh
  161. #include <Shape.xh>
  162. #endif
  163.  
  164. #ifndef SOM_Module_OpenDoc_StdDefs_defined
  165. #include <StdDefs.xh>
  166. #endif
  167.  
  168. #ifndef SOM_Module_OpenDoc_StandardExtensions_defined
  169. #include <StdExts.xh>
  170. #endif
  171.  
  172. #ifndef SOM_Module_OpenDoc_StdProps_defined
  173. #include <StdProps.xh>
  174. #endif
  175.  
  176. #ifndef SOM_Module_OpenDoc_StdTypes_defined
  177. #include <StdTypes.xh>
  178. #endif
  179.  
  180. #ifndef SOM_ODStorageUnit_xh
  181. #include <StorageU.xh>
  182. #endif
  183.  
  184. #ifndef SOM_ODStorageUnitView_xh
  185. #include <SUView.xh>
  186. #endif
  187.  
  188. #ifndef SOM_ODTransform_xh
  189. #include <Trnsform.xh>
  190. #endif
  191.  
  192. #ifndef SOM_ODTranslation_xh
  193. #include <Translt.xh>
  194. #endif
  195.  
  196. #ifndef SOM_ODTypeList_xh
  197. #include <TypeList.xh>
  198. #endif
  199.  
  200. #ifndef SOM_ODWindow_xh
  201. #include <Window.xh>
  202. #endif
  203.  
  204. #ifndef SOM_ODWindowState_xh
  205. #include <WinStat.xh>
  206. #endif
  207.  
  208. // -- OpenDoc Utilities --
  209.  
  210. #ifndef _BNDNSUTL_
  211. #include <BndNSUtl.h>
  212. #endif
  213.  
  214. #ifndef _FOCUSLIB_
  215. #include <FocusLib.h>
  216. #endif
  217.  
  218. #ifndef _INFOUTIL_
  219. #include <InfoUtil.h>
  220. #endif
  221.  
  222. #ifndef _ISOSTR_
  223. #include <ISOStr.h>
  224. #endif
  225.  
  226. #ifndef _ITEXT_
  227. #include <IText.h>
  228. #endif
  229.  
  230. #ifndef _ODMEMORY_
  231. #include <ODMemory.h>
  232. #endif
  233.  
  234. #ifndef _ODUTILS_
  235. #include <ODUtils.h>
  236. #endif
  237.  
  238. #ifndef _STDTYPIO_
  239. #include <StdTypIO.h>
  240. #endif
  241.  
  242. #ifndef _STORUTIL_
  243. #include <StorUtil.h>
  244. #endif
  245.  
  246. #ifndef _TEMPITER_
  247. #include <TempIter.h>
  248. #endif
  249.  
  250. #ifndef _TEMPOBJ_
  251. #include <TempObj.h>
  252. #endif
  253.  
  254. #ifndef _USERSRCM_
  255. #include <UseRsrcM.h>
  256. #endif
  257.  
  258. #ifndef _WINUTILS_
  259. #include <WinUtils.h>
  260. #endif
  261.  
  262. // -- Macintosh Includes --
  263.  
  264. #ifndef __DIALOGS__
  265. #include <Dialogs.h>
  266. #endif
  267.  
  268. #ifndef __DRAG__
  269. #include <Drag.h>
  270. #endif
  271.  
  272. #ifndef __FILES__
  273. #include <Files.h>
  274. #endif
  275.  
  276. #ifndef __GXMATH__
  277. #include <GXMath.h>
  278. #endif
  279.  
  280. #ifndef __ICONS__
  281. #include <Icons.h>
  282. #endif
  283.  
  284. #ifndef __TOOLUTILS__
  285. #include <ToolUtils.h>
  286. #endif
  287.  
  288. #ifndef __QUICKDRAW__
  289. #include <Quickdraw.h>
  290. #endif
  291.  
  292.  
  293. #pragma segment SoundEditor
  294.  
  295. //==============================================================================
  296. #pragma mark    • Multiple Kind Utility Functions •
  297. //==============================================================================
  298.  
  299. //------------------------------------------------------------------------------
  300. // Function:    IsKindSupported
  301. // Origin:        SoundEditor
  302. //
  303. // Description:    Return a boolean result indicating whether the given kind 
  304. //                token is supported by SoundEditor.
  305. //------------------------------------------------------------------------------
  306.  
  307. static ODBoolean IsKindSupported( Environment*        ev, 
  308.                                   ODTypeToken        token )
  309. {
  310.     return (token == gGlobals->fNativeData 
  311.          || token == gGlobals->fMacSndData);
  312. }
  313.  
  314. //------------------------------------------------------------------------------
  315. // Function:    GetKindFromToken
  316. // Origin:        SoundEditor
  317. //
  318. // Description:    Given a token, return the type that it corresponds to.
  319. //------------------------------------------------------------------------------
  320.  
  321. static ODType GetKindFromToken( Environment*        ev, 
  322.                                 ODTypeToken            token)
  323. {
  324.     WASSERT(IsKindSupported(ev, token));
  325.  
  326.     if ( token == gGlobals->fNativeData )
  327.         return kSoundEditorKind;
  328.  
  329.     else if ( token == gGlobals->fMacSndData )
  330.         return gGlobals->fSndDataKind;
  331.     
  332.     else
  333.         return kODNULL;
  334. }
  335.  
  336. //------------------------------------------------------------------------------
  337. // Function:    SetPreferredKind
  338. // Origin:        SoundEditor
  339. //
  340. // Description:    This function will set the preferred kind property of the
  341. //                given storage unit to the given type.
  342. //------------------------------------------------------------------------------
  343.  
  344. static void SetPreferredKind( Environment*        ev, 
  345.                               ODStorageUnit*    storageUnit,
  346.                               ODType            kind )
  347. {
  348.     TRY
  349.         if ( kind == kODNULL )
  350.             ODSURemoveProperty(ev, storageUnit, kODPropPreferredKind);
  351.         else
  352.             ODSetISOStrProp(ev, storageUnit, kODPropPreferredKind, kODISOStr, kind);
  353.     CATCH_ALL
  354.         // Remove the property and value if something went wrong.
  355.         ODSURemoveProperty(ev, storageUnit, kODPropPreferredKind);
  356.     ENDTRY
  357. }
  358.  
  359. //------------------------------------------------------------------------------
  360. // Function:    GetKindToInternalize
  361. // Origin:        SoundEditor
  362. //
  363. // Description:    Returns a token representing the kind of data that should be
  364. //                internalized from the given storage unit.
  365. //------------------------------------------------------------------------------
  366.  
  367. static ODTypeToken GetKindToInternalize( Environment*        ev,
  368.                                          ODSession*            session, 
  369.                                          ODStorageUnit*        storageUnit )
  370. {
  371.     // This should become a token representing the kind of data that should be
  372.     // internalized from the given storage unit.
  373.     ODTypeToken kindToUse = kODNullTypeToken;
  374.  
  375.     TempODISOStr docPrefKindStr = ODGetISOStrProp(ev, storageUnit, kODPropPreferredKind, 
  376.                                                    kODISOStr, kODNULL, kODNULL);        
  377.     
  378.     ODTypeToken docPrefKind = kODNullTypeToken;
  379.     if (docPrefKindStr != kODNULL)
  380.         docPrefKind = session->Tokenize(ev, docPrefKindStr);
  381.     
  382.     // If the data has a preferred kind, see if we can use it.
  383.     if ( docPrefKind != kODNullTypeToken && IsKindSupported(ev, docPrefKind)
  384.          && storageUnit->Exists(ev, kODPropContents, GetKindFromToken(ev, docPrefKind), 0) )
  385.     {
  386.         kindToUse = docPrefKind;
  387.     }
  388.     
  389.     // If there is not document-preferred kind, we don't support it, or it wasn't found,
  390.     // find the first value (the one with highest fidelity) that we understand.
  391.     if (kindToUse == kODNullTypeToken)
  392.     {
  393.         storageUnit->Focus(ev, kODPropContents, kODPosUndefined, kODNULL, 0, kODPosAll);
  394.         ODULong numValues = storageUnit->CountValues(ev);
  395.         
  396.         for (ODULong index = 1; index <= numValues; index++)
  397.         {
  398.             // Index from n to 1 through the values -- lowest fidelity to highest.
  399.             storageUnit->Focus(ev, kODPropContents, kODPosUndefined, kODNULL, 
  400.                                index, kODPosUndefined);
  401.             TempODValueType value = storageUnit->GetType(ev);
  402.             
  403.             ODTypeToken valueToken = session->Tokenize(ev, value);
  404.             if ( IsKindSupported(ev, valueToken) )
  405.             {
  406.                 kindToUse = valueToken;
  407.                 break;
  408.             }
  409.         }
  410.     }
  411.  
  412.     return kindToUse;
  413. }
  414.  
  415.  
  416. //==============================================================================
  417. // SoundEditor
  418. //==============================================================================
  419.  
  420. //==============================================================================
  421. #pragma mark    • Construction and Destruction •
  422. //==============================================================================
  423.  
  424. //------------------------------------------------------------------------------
  425. // Method:        Constructor
  426. // Origin:        SoundEditor
  427. //
  428. // Description:    This is the C++ class constructor.
  429. //
  430. // Warnings:    You are not allowed to throw an exception from this method.
  431. //------------------------------------------------------------------------------
  432.  
  433. SoundEditor::SoundEditor()
  434. {
  435.     SOM_Trace("SoundEditor","Constructor");
  436.     
  437.     fDisplayFrames        = kODNULL;
  438.     fSettingsExtension    = kODNULL;
  439.     fSemIntf            = kODNULL;
  440.     
  441.     fSession            = kODNULL;
  442.     fSelf                = kODNULL;
  443.  
  444.     fDirty                = kODFalse;
  445.     fReadOnlyStorage    = kODFalse;
  446.     fPreferredKind        = kODNullTypeToken;
  447.     
  448.     fPlayer                = kODNULL;
  449.     fRecorder            = kODNULL;
  450.  
  451.     fMenuSavedResFile    = -1;
  452.  
  453.     fRecordingQuality    = siGoodQuality;
  454.     fCurrentTime        = 0;
  455.     
  456.     fMaxMemory            = 1024000;
  457.     fMaxSeconds            = 0;
  458.     fSecsRecorded        = 0;
  459.     fUnknownSound        = kODTrue;
  460. }
  461.  
  462. //------------------------------------------------------------------------------
  463. // Method:        Destructor
  464. // Origin:        SoundEditor
  465. //
  466. // Description:    This is the C++ class destructor.
  467. //
  468. // Warnings:    You are not allowed to throw an exception from this method.
  469. //------------------------------------------------------------------------------
  470.  
  471. SoundEditor::~SoundEditor()
  472. {
  473.     SOM_Trace("SoundEditor","Destructor");
  474. }
  475.  
  476. //==============================================================================
  477. #pragma mark    • Initialization •
  478. //==============================================================================
  479.  
  480. //------------------------------------------------------------------------------
  481. // Method:        InitPart
  482. // Origin:        ODPart
  483. //
  484. // Description:    This method is called when a new instance of this part is being
  485. //                created. The part should prepare itself to run.
  486. //
  487. // Parent:        The part's parent class was called before this method was
  488. //                dispatched to (see som_SamplePart.cpp).
  489. //
  490. // Warning:        It is not appropriate to require user interaction while
  491. //                stationery is being created. Do not present the user with error
  492. //                dialogs or splash screens from this method.
  493. //------------------------------------------------------------------------------
  494.  
  495. void SoundEditor::InitPart( Environment*    ev,
  496.                             ODStorageUnit*    storageUnit,
  497.                             ODPart*            partWrapper )
  498. {
  499.     SOM_Trace("SoundEditor","InitPart");
  500.  
  501.     // To allow editor swapping (translation) at runtime, OpenDoc requires
  502.     // that we pass in a "reference" to ourselves when interacting with the
  503.     // API (e.g. WindowState::RegisterWindow(), Dispatcher::RegisterIdle, etc).
  504.     // The "partWrapper" passed to us here and in InitPartFromStorage is the
  505.     // "reference" OpenDoc is asking us to use.
  506.     fSelf = partWrapper;
  507.     
  508.     // We are being created, either as part of generating stationery or
  509.     // by some editor instantiating the part, so the destination storage
  510.     // must be writeable.
  511.     fReadOnlyStorage = kODFalse;
  512.     
  513.     // Call the common initialization code to get set up.
  514.     this->Initialize(ev);
  515.     
  516.     // Since we're making stationery, we will select the default kind
  517.     // as the preferred kind.
  518.     fPreferredKind = gGlobals->fNativeData;
  519.  
  520.     // Since we have just been created, our state/content info has
  521.     // never been written out, so setting our "dirty" flag will
  522.     // give us a chance to do that.
  523.     this->SetDirty(ev);
  524.  
  525.     // If an exception is thrown, clean up will occur in the destructor 
  526.     // which will be called shortly after the error is raised.
  527. }
  528.  
  529. //------------------------------------------------------------------------------
  530. // Method:        InitPartFromStorage
  531. // Origin:        ODPart
  532. //
  533. // Description:    This method is called when a document/stationery is being opened
  534. //                or when the part is internalized by its containing part. The
  535. //                part should merely read in the saved state/content and
  536. //                initialize itself. The part must not alter its storage unit;
  537. //                otherwise, the "Save" menu item becomes enabled without the user
  538. //                actually having made a change to the document.
  539. //
  540. // Parent:        The part's parent class was called before this method was
  541. //                dispatched to (see som_SamplePart.cpp).
  542. //------------------------------------------------------------------------------
  543.  
  544. void SoundEditor::InitPartFromStorage( Environment*        ev,
  545.                                        ODStorageUnit*    storageUnit,
  546.                                        ODPart*            partWrapper )
  547. {
  548.     SOM_Trace("SoundEditor","InitPartFromStorage");
  549.  
  550.     // To allow editor swapping (translation) at runtime, OpenDoc requires
  551.     // that we pass in a "reference" to ourselves when interacting with the
  552.     // API (e.g. WindowState::RegisterWindow(), Dispatcher::RegisterIdle, etc).
  553.     // The "partWrapper" passed to us here and in InitPart is the
  554.     // "reference" OpenDoc is asking us to use.
  555.     fSelf = partWrapper;
  556.         
  557.     // Are we being opened from a read-only draft? If so, we cannot
  558.     // write anything back out to our storage unit.
  559.     fReadOnlyStorage = (ODGetDraft(ev,storageUnit)->GetPermissions(ev)
  560.                             < kODDPSharedWrite);
  561.     
  562.     // Call the common initialization code to get set up.
  563.     this->Initialize(ev);
  564.  
  565.     // Read in the state the part was in when it was last Externalized.
  566.     // This allows the part to present the same "environment" the user
  567.     // had the part set up in the last time it was edited.
  568.     this->InternalizeStateInfo(ev, storageUnit);
  569.  
  570.     // Read in the content annotation.
  571.     this->InternalizeAnnotation(ev, storageUnit);
  572.  
  573.     // Read in the contents for your part editor.
  574.     this->InternalizeContent(ev, storageUnit);
  575.  
  576.     // If an exception is thrown, clean up will occur in the destructor 
  577.     // which will be called shortly after the error is raised.
  578. }
  579.  
  580. //------------------------------------------------------------------------------
  581. // Method:        Initialize
  582. // Origin:        SoundEditor
  583. //
  584. // Description:    This method is called during the initialization of the part. The
  585. //                method is used to initialize all fields of the part and to
  586. //                convert ISO string types to tokens for faster comparisons 
  587. //                throughout the code.
  588. //
  589. //                If an exception is thrown in this method, it wil be propogated
  590. //                back to OpenDoc which will call our ReleaseAll() method and the
  591. //                class destructor. All memory allocated here will be cleaned up
  592. //                in the ReleaseAll() method.
  593. //------------------------------------------------------------------------------
  594.  
  595. void SoundEditor::Initialize( Environment*    ev )
  596. {
  597.     SOM_Trace("SoundEditor","Initialize");
  598.  
  599.     // Grab a reference to the Session object. This is merely for
  600.     // convenience.
  601.     fSession = ODGetSession(ev, fSelf);
  602.     
  603.     // Create a list to keep track of the frames we are being
  604.     // displayed in. Also used for maintenance (i.e., Purging memory).
  605.     fDisplayFrames = new CList;
  606.  
  607.     // Create the CPlayer and CRecorder helper classes to handle
  608.     // recording and playing sound
  609.     fPlayer = new CPlayer;
  610.     fRecorder = new CRecorder;
  611.  
  612.     // First check to see if the library's global variables have
  613.     // been initialized (meaning another part instantiation is already
  614.     // running).
  615.  
  616.     if ( gGlobalsUsageCount == 0 )
  617.     {
  618.         // Create our globals space. We store the globals in a struct so
  619.         // that we can put them in temp mem. Otherwise, CFM loads the globals
  620.         // with the data fragment of a CFM library in the application heap.
  621.         gGlobals = new SoundEditorGlobals;
  622.     
  623.         // We will be using the following foci (shared resources) in this
  624.         // part. For convenience, we tokenize the values here and store
  625.         // them for equivalence tests in the activation methods.
  626.         gGlobals->fSelectionFocus    = fSession->Tokenize(ev, kODSelectionFocus);
  627.         gGlobals->fMenuFocus        = fSession->Tokenize(ev, kODMenuFocus);
  628.         gGlobals->fKeyFocus            = fSession->Tokenize(ev, kODKeyFocus);
  629.         gGlobals->fModalFocus        = fSession->Tokenize(ev, kODModalFocus);
  630.         gGlobals->fSoundInFocus        = fSession->Tokenize(ev, kSoundInFocus);
  631.         gGlobals->fSoundOutFocus    = fSession->Tokenize(ev, kSoundOutFocus);
  632.         gGlobals->fClipboardFocus    = fSession->Tokenize(ev, kODClipboardFocus);
  633.         
  634.         // Also for convenience, we tokenize our part's main presentation
  635.         // and the standard view types.
  636.         gGlobals->fControlsPresentation    = fSession->Tokenize(ev, kControlsPresentation);
  637.     
  638.         gGlobals->fFrameView         = fSession->Tokenize(ev, kODViewAsFrame);
  639.         gGlobals->fLargeIconView     = fSession->Tokenize(ev, kODViewAsLargeIcon);
  640.         gGlobals->fSmallIconView     = fSession->Tokenize(ev, kODViewAsSmallIcon);
  641.         gGlobals->fThumbnailView     = fSession->Tokenize(ev, kODViewAsThumbnail);
  642.     
  643.         // The Sound Editor defines the "Sound in" and "Sound out" ports as
  644.         // shared resources. To allow sharing of these resources, we register them
  645.         // with the Arbitrator.  NOTE: By passing kODNULL as the focus module in 
  646.         // the RegisterFocus call, we are telling OpenDoc to use the default
  647.         // single-owner type of focus module.
  648.         ODArbitrator* arbitrator = fSession->GetArbitrator(ev);
  649.         if ( !arbitrator->IsFocusRegistered(ev, gGlobals->fSoundInFocus) )
  650.             arbitrator->RegisterFocus(ev, gGlobals->fSoundInFocus, kODNULL);
  651.         if ( !arbitrator->IsFocusRegistered(ev, gGlobals->fSoundOutFocus) )
  652.             arbitrator->RegisterFocus(ev, gGlobals->fSoundOutFocus, kODNULL);
  653.  
  654.         // Lastly, we will package the following foci
  655.         // so that we can request the "set" at activation time.
  656.         gGlobals->fUIFocusSet = arbitrator->CreateFocusSet(ev);
  657.         gGlobals->fUIFocusSet->Add(ev, gGlobals->fMenuFocus);
  658.         gGlobals->fUIFocusSet->Add(ev, gGlobals->fKeyFocus);
  659.         gGlobals->fUIFocusSet->Add(ev, gGlobals->fSelectionFocus);
  660.             
  661.         // The part supports importing sound files and data, so we must
  662.         // get the value types OpenDoc associates with each. To do
  663.         // this, we must call the Translation object.
  664.         gGlobals->fSndDataKind = fSession->GetTranslation(ev)->
  665.                 GetISOTypeFromPlatformType(ev, 'snd ', kODPlatformDataType);
  666.                         
  667.         gGlobals->fSndFileKind = fSession->GetTranslation(ev)->
  668.                 GetISOTypeFromPlatformType(ev, 'sfil', kODPlatformFileType);
  669.     
  670.         gGlobals->fAppleHFSFlavor = fSession->GetTranslation(ev)->
  671.                 GetISOTypeFromPlatformType(ev, 'hfs ', kODPlatformDataType);
  672.                         
  673.         // Tokenize the content value types that our part supports so
  674.         // we can more easily support writing data in the preferred kind.
  675.         gGlobals->fNativeData = fSession->Tokenize(ev, kSoundEditorKind);
  676.         gGlobals->fMacSndData = fSession->Tokenize(ev, gGlobals->fSndDataKind);
  677.         gGlobals->fMacSndFile = fSession->Tokenize(ev, gGlobals->fSndFileKind);
  678.         
  679.         // Add our resource-based menus and register all the editor specific
  680.         // command numbers.
  681.         this->BuildMenuBar(ev);
  682.         
  683.         // Determine what Script/Language the part is localized for.
  684.         // This is important/necessary for creating OpenDoc's text objects.
  685.         GetEditorScriptLanguage(ev, &gGlobals->fEditorsScript, &gGlobals->fEditorsLanguage);
  686.         
  687.         // The first client of the global variables is running.
  688.         gGlobalsUsageCount = 1;
  689.     }
  690.     else
  691.     // If the globals have been initialized, we keep a "usage" count so that 
  692.     // we can null out the global variables when we are finished using them.
  693.     {
  694.         gGlobalsUsageCount++;
  695.     }
  696. }
  697.  
  698. //==============================================================================
  699. #pragma mark    • Storage •
  700. //==============================================================================
  701.  
  702. //------------------------------------------------------------------------------
  703. // Method:        Release
  704. // Origin:        ODPart
  705. //
  706. // Description:    This method is called each time an object releases a reference
  707. //                to the part. If the refcount falls to 0, the part should
  708. //                release the fSelf part reference.
  709. //
  710. // Parent:        The part's parent class was called before this method was
  711. //                dispatched to (see som_SamplePart.cpp).
  712. //
  713. // Warning:        If the part releases any other object when the refcount falls to
  714. //                zero, it will need to override the IncrementRefCount method so
  715. //                that the object can be referenced again if the parts refcount
  716. //                should be incremented before it is deleted.
  717. //------------------------------------------------------------------------------
  718.  
  719. void SoundEditor::Release( Environment* ev )
  720. {
  721.     SOM_Trace("SoundEditor","Release");
  722.  
  723.     if (fSelf->GetRefCount(ev) == 0)
  724.         ODGetDraft(ev, fSelf)->ReleasePart(ev, fSelf);
  725. }
  726.  
  727. //------------------------------------------------------------------------------
  728. // Method:        ReleaseAll
  729. // Origin:        ODPart
  730. //
  731. // Description:    This method is called just prior to the part being deleted by
  732. //                the Draft. The part must release all references to all
  733. //                refcounted objects it has stored internally; not doing so, will
  734. //                cause an "invalid ref count" exception/error at some later time.
  735. //
  736. // Parent:        The part's parent class will be called after this method
  737. //                returns (see som_SamplePart.cpp).
  738. //------------------------------------------------------------------------------
  739.  
  740. void SoundEditor::ReleaseAll( Environment* ev )
  741. {
  742.     SOM_Trace("SoundEditor","ReleaseAll");
  743.  
  744.     // Release our extensions.  Note: we can't use ODReleaseObject or 
  745.     // ODFinalReleaseObject here because they null out the field before the
  746.     // call to Release, but we need the field intact when ReleaseExtension is 
  747.     // called back upon the last reference being released.
  748.     ODRelease(ev, fSettingsExtension);
  749.     ODRelease(ev, fSemIntf);
  750.  
  751.     // Delete the player and recorder helper objects
  752.     ODDeleteObject(fPlayer);
  753.     ODDeleteObject(fRecorder);
  754.  
  755.     // If the last part instance using the globals is released,
  756.     // we need to delete the globals.
  757.     if ( --gGlobalsUsageCount == 0 )
  758.     {
  759.         // The FocusSet is not shared and deleting it is the right thing 
  760.         // to do.
  761.         ODDeleteObject(gGlobals->fUIFocusSet);
  762.         
  763.         // Dispose the memory used by our ISO strings.
  764.         ODDisposePtr(gGlobals->fSndDataKind);
  765.         ODDisposePtr(gGlobals->fSndFileKind);
  766.         ODDisposePtr(gGlobals->fAppleHFSFlavor);
  767.         
  768.         // We need to remove our menus from the menubar and dispose of
  769.         // them to free up their memory.
  770.         for (ODSShort index = 0; index < kNumMenus; index++)
  771.         {
  772.             ODSShort id = kBaseMenuID + index;
  773.             
  774.             MenuHandle menu = gGlobals->fMenuBar->GetMenu(ev, id);
  775.             gGlobals->fMenuBar->RemoveMenu(ev, id);
  776.             if ( menu != kODNULL )
  777.             {
  778.                 CUsingLibraryResources res;
  779.                 ReleaseResource((Handle) menu);
  780.             }
  781.         }
  782.  
  783.         // Release the menubar.
  784.         ODReleaseObject(ev, gGlobals->fMenuBar);
  785.         
  786.         // Dispose the controls picture.
  787.         if ( gGlobals->fControlsPict != kODNULL )
  788.             ODDisposeHandle(gGlobals->fControlsPict);
  789.         
  790.         // Dispose the thumbnail picture.
  791.         if ( gGlobals->fThumbnail != kODNULL )
  792.             ReleaseResource(gGlobals->fThumbnail);
  793.         
  794.         // Now, clean up the globals struct.
  795.         ODDeleteObject(gGlobals);
  796.     }
  797.     
  798.     // Remove and release our display frames.
  799.     if ( fDisplayFrames )
  800.     {
  801.         CListIterator fiter(fDisplayFrames);
  802.         for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
  803.                 fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
  804.         {
  805.             // Delete the proxy object and its contents. The frame's
  806.             // refcount will be decremented in the proxy destructor.
  807.             fiter.RemoveCurrent();
  808.             delete proxy;
  809.         }
  810.     
  811.         // Delete the display frame collection.
  812.         ODDeleteObject(fDisplayFrames);
  813.     }
  814.     
  815.     // If something goes wrong while we are cleaning up, we must
  816.     // let the Draft now because there may be some refcounted objects
  817.     // which did not get released. Not to mention, possible memory
  818.     // leaks.
  819. }
  820.  
  821. //------------------------------------------------------------------------------
  822. // Method:        Purge
  823. // Origin:        ODPart
  824. //
  825. // Description:    This method is called when the OpenDoc requires more memory for
  826. //                allocating objects and just before a part is deleted. The part
  827. //                should free up as much memory as it can.
  828. //
  829. //                The part determines which views are being "used" in its display
  830. //                frames. The resources for the unused view types are then purged.
  831. //------------------------------------------------------------------------------
  832.  
  833. ODSize SoundEditor::Purge( Environment*        ev,
  834.                            ODSize            size )
  835. {
  836.     SOM_Trace("SoundEditor","Purge");
  837.  
  838.     // Purge is called during the creation of stationery. However,
  839.     // we have not create our internal display frames list, so 
  840.     // trying to iterate over it would be futile.
  841.     if ( fDisplayFrames == kODNULL ) return 0;
  842.     
  843.     ODSize         bytesFreed        = 0;
  844.     ODBoolean    usingThumbnail  = kODFalse;
  845.     
  846.     // Iterate over the frames we are displayed through and determine which
  847.     // view types are currently in use.
  848.     
  849.     CListIterator fiter(fDisplayFrames);
  850.     for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
  851.             fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
  852.     {
  853.         // If the display frame is real (has been "connected" or was "added")
  854.         // get its view type; otherwise, ignore it.
  855.         if ( proxy->FrameIsLoaded(ev) )
  856.         {
  857.             ODTypeToken    frameView = proxy->GetFrame(ev)->GetViewType(ev);
  858.         
  859.             if ( frameView == gGlobals->fThumbnailView )
  860.                 usingThumbnail = kODTrue;
  861.             
  862.             // Release the frame reference, but don't get rid of the
  863.             // proxy object because we're not done with the frame. If
  864.             // all parts release their references the frame will be
  865.             // purged from memory.
  866.             proxy->Purge(ev);
  867.         }
  868.     }
  869.     
  870.     // Based on the usage of the supported view types, free up as much
  871.     // memory as possible.
  872.  
  873.     if ( !usingThumbnail && gGlobals->fThumbnail != kODNULL )
  874.     {
  875.         bytesFreed += ODGetHandleSize(gGlobals->fThumbnail);
  876.         ReleaseResource(gGlobals->fThumbnail);
  877.         gGlobals->fThumbnail = kODNULL;
  878.     }
  879.     
  880.     return bytesFreed;
  881. }
  882.  
  883. //------------------------------------------------------------------------------
  884. // Method:        InternalizeStateInfo
  885. // Origin:        SoundEditor
  886. //
  887. // Description:    This method is used to read in "state" information for the part.
  888. //                This is information related to the workings of the part editor,
  889. //                not the content.
  890. //
  891. //                The part writes out a list of weak references to its display
  892. //                frames. This allows the part to reuse the same display frames
  893. //                each time the document is opened. Those references are read
  894. //                back in and validated here.
  895. //
  896. // Note:        The function StorageUnitGetValue simplifies the use of
  897. //                ODByteArrary, which is required the StorageUnit interface. Look
  898. //                in StorUtil.h/cpp for an example of using the ODByteArray struct.
  899. //------------------------------------------------------------------------------
  900.  
  901. void SoundEditor::InternalizeStateInfo( Environment*    ev,
  902.                                         ODStorageUnit*    storageUnit )
  903. {
  904.     SOM_Trace("SoundEditor","InternalizeStateInfo");
  905.  
  906.     ODStorageUnitRef    weakRef;
  907.     ODULong                size;
  908.  
  909.     // Internalize the part's display frame list.
  910.  
  911.     if ( ODSUExistsThenFocus(ev, storageUnit, kODPropDisplayFrames, kODWeakStorageUnitRefs) )
  912.     {
  913.         size = storageUnit->GetSize(ev);
  914.         storageUnit->SetOffset(ev, 0);    
  915.     
  916.         for ( ODULong offset = 0; offset < size; offset += kODStorageUnitRefSize )
  917.         {        
  918.             TRY    
  919.                 StorageUnitGetValue(storageUnit, ev, kODStorageUnitRefSize, (ODPtr) &weakRef);
  920.                 
  921.                 if ( storageUnit->IsValidStorageUnitRef(ev, weakRef) )
  922.                 {        
  923.                     // We lazily internalize our display frames, meaning we don't "get"
  924.                     // the frame until we absolutely need it. This reduces the time
  925.                     // to internalize the part and the amount of memory needed.
  926.                     
  927.                     // Convert the reference into a runtime id.
  928.                     ODID frameID = storageUnit->GetIDFromStorageUnitRef(ev, weakRef);
  929.  
  930.                     // Create a proxy class to support the lazy internalization.
  931.                     CFrameProxy* proxy = new CFrameProxy;
  932.                     proxy->InitFrameProxy(frameID, ODGetDraft(ev, storageUnit));
  933.  
  934.                     // Add the proxy to the display frame collection.
  935.                     fDisplayFrames->Add(proxy);
  936.                 }
  937.             CATCH_ALL
  938.                 // consume exception
  939.             ENDTRY
  940.         }
  941.     }
  942. }
  943.  
  944. //------------------------------------------------------------------------------
  945. // Method:        InternalizeAnnotation
  946. // Origin:        SoundEditor
  947. //
  948. // Description:    This method is used to read in content annotation for the part.
  949. //------------------------------------------------------------------------------
  950.  
  951. void SoundEditor::InternalizeAnnotation( Environment*    ev,
  952.                                          ODStorageUnit*    storageUnit )
  953. {
  954.     SOM_Trace("SoundEditor","InternalizeAnnotation");
  955.  
  956.     // Internalize the default recording quality.
  957.     
  958.     fUnknownSound = kODTrue;                // unknown sound quality for now
  959.     fRecorder->SetQuality(siGoodQuality);    // use default values
  960.     
  961.     if ( ODSUExistsThenFocus(ev, storageUnit, kPropRecordingQuality, kOSType) )
  962.     {
  963.         if ( storageUnit->GetSize(ev) > 0 )
  964.         {
  965.             TRY
  966.                 OSType quality;
  967.                 StorageUnitGetValue(storageUnit, ev, sizeof(OSType), &quality);
  968.                         
  969.                 // If the value we read in does not match one of the quality
  970.                 // setting we know about, then either new quality recording
  971.                 // levels have been added to the Sound Manager, or another
  972.                 // editor was bound to our content and wrote the value 
  973.                 // incorrectly.
  974.                         
  975.                 if ( quality == siGoodQuality   ||
  976.                      quality == siBetterQuality ||
  977.                      quality == siBestQuality)
  978.                 {
  979.                     fUnknownSound = kODFalse;
  980.                     fRecordingQuality = quality;
  981.                     fRecorder->SetQuality(quality);
  982.                 }
  983.             CATCH_ALL
  984.                 // Consume exception.  We can survive if we don't know the quality.
  985.             ENDTRY
  986.         }
  987.     }
  988.     
  989.     // Find out the maximum number of seconds to record at current quality
  990.     // even if original was unknown, can still give current max time
  991.     fMaxSeconds = fRecorder->GetMaxTime(fMaxMemory, fRecordingQuality);
  992.  
  993.     // Internalize the recorded sound length.
  994.     
  995.     if ( ODSUExistsThenFocus(ev, storageUnit, kPropSecsRecorded, kODULong) )
  996.     {
  997.         if ( storageUnit->GetSize(ev) > 0 )
  998.         {
  999.             TRY
  1000.                 fSecsRecorded = ODGetULongProp(ev, storageUnit, kPropSecsRecorded, kODULong);
  1001.                     // Here we use a routine from StdTypIO.h, an OpenDoc Utility to simplify
  1002.                     // outputting standard data types.  This routine also will perform endian-
  1003.                     // swapping to put the value in the right form to be standard.
  1004.             CATCH_ALL
  1005.                 fSecsRecorded = -1;
  1006.                 fUnknownSound = kODTrue;
  1007.             ENDTRY
  1008.         }
  1009.     }
  1010.     else    // original recording length is unknown
  1011.     {
  1012.         fSecsRecorded = -1;        
  1013.         fUnknownSound = kODTrue;
  1014.     }
  1015. }
  1016.  
  1017. //------------------------------------------------------------------------------
  1018. // Method:        InternalizeContent
  1019. // Origin:        SoundEditor
  1020. //
  1021. // Description:    This method is called during initialization of the part from an
  1022. //                existing document. The content of the part should be read in.
  1023. //
  1024. //                The part has no intrinsic content, so the part does nothing.
  1025. //                The method is here for completeness.
  1026. //------------------------------------------------------------------------------
  1027.  
  1028. void SoundEditor::InternalizeContent( Environment*        ev,
  1029.                                       ODStorageUnit*    storageUnit )
  1030. {
  1031.     SOM_Trace("SoundEditor","InternalizeContent");
  1032.     
  1033.     // Get a token representing the kind of data that should be internalized
  1034.     // from the given storage unit.
  1035.     ODTypeToken kindToUse = GetKindToInternalize(ev, fSession, storageUnit);
  1036.  
  1037.     // We support the old style Macintosh sound files and need to
  1038.     // check for them when we are being instantiated from an
  1039.     // existing strorage unit. We do this because the Drag&Drop code
  1040.     // will add a value for the dropped file's type and a HFSFlavor value
  1041.     // so that the file can be accessed by the target part.
  1042.     if ( (kindToUse == kODNullTypeToken || kindToUse == gGlobals->fMacSndFile)
  1043.          && ODSUExistsThenFocus(ev, storageUnit, kODPropContents, gGlobals->fAppleHFSFlavor) )
  1044.     {
  1045.         ODULong size = storageUnit->GetSize(ev);
  1046.         
  1047.         if ( size > 0 )
  1048.         {
  1049.             HFSFlavor* hfsInfo = (HFSFlavor*) ODNewPtrClear(size);
  1050.             TempODPtr autoDisposer = (ODPtr) hfsInfo;
  1051.                 // This is used so that the memory block will be automatically
  1052.                 // disposed when it goes out of scope or in case an exception
  1053.                 // is thrown. 
  1054.             
  1055.             StorageUnitGetValue(storageUnit, ev, size, hfsInfo);
  1056.             
  1057.             if (hfsInfo->fileType == kSoundFileKind)
  1058.             {
  1059.                 FSSpec fileSpec = hfsInfo->fileSpec;
  1060.                 ODSShort refNum = FSpOpenResFile(&fileSpec, fsRdPerm);
  1061.                 
  1062.                 if ( refNum != -1 && !ResError() )
  1063.                 {
  1064.                     TRY
  1065.                         UseResFile(refNum);
  1066.                         
  1067.                         TRY
  1068.                             // We use the utility function Read1IndResource to read
  1069.                             // the resource into memory.  We use this rather than just
  1070.                             // calling Get1IndResource because Get1IndResource would
  1071.                             // put the resoure in the application heap, and we don't
  1072.                             // have much room available in the application heap.  It also
  1073.                             // detaches the resource from the resource fork it came from.
  1074.                             ODHandle sound = Read1IndResource('snd ', 1);
  1075.                             
  1076.                             fPlayer->SetSound(sound);
  1077.                             this->SetDirty(ev);
  1078.                             
  1079.                             // Remember the "preferred" kind for the data to be written as.
  1080.                             if (fPreferredKind == kODNullTypeToken)
  1081.                                 fPreferredKind = gGlobals->fMacSndData;
  1082.                             
  1083.                             // Write out the preferred kind.
  1084.                             SetPreferredKind(ev, storageUnit, GetKindFromToken(ev, fPreferredKind));
  1085.  
  1086.                         CATCH_ALL
  1087.                             fPlayer->SetSound(kODNULL);
  1088.                             fSecsRecorded = 0;
  1089.                             // Consume the exception.
  1090.                         ENDTRY
  1091.                     CATCH_ALL
  1092.                         CloseResFile(refNum);
  1093.                         RERAISE;
  1094.                     ENDTRY
  1095.                     
  1096.                     CloseResFile(refNum);
  1097.                 }
  1098.             }
  1099.         }
  1100.     }
  1101.     else if ( kindToUse != kODNullTypeToken )
  1102.     {
  1103.         // We support the old-style Macintosh sound scrap type "snd "
  1104.         // and need to check for it when we are being instantiated
  1105.         // from an existing strorage unit.
  1106.         
  1107.         // If we are not importing old-style data, we must be
  1108.         // reading our own data format.
  1109.  
  1110.         ODSUExistsThenFocus(ev, storageUnit, kODPropContents, GetKindFromToken(ev, kindToUse));
  1111.             // We already know it exists, but this is a convenient way to focus.
  1112.         
  1113.         ODULong soundSize = storageUnit->GetSize(ev);
  1114.         if (soundSize > 0)
  1115.         {
  1116.             ODHandle tempHandle = ODNewHandle(soundSize);
  1117.             
  1118.             // Allocate memory for the new sound data. If we are
  1119.             // successful, dispose of any old sound data and assign
  1120.             // the new storage to our part field.
  1121.             fPlayer->SetSound(tempHandle);
  1122.             
  1123.             TempHandleLockAndRememberState lock(fPlayer->GetSound());
  1124.             ODValue soundDataPtr = *lock;
  1125.             StorageUnitGetValue(storageUnit, ev, soundSize, soundDataPtr);
  1126.         }
  1127.         else
  1128.         {
  1129.             fPlayer->SetSound(kODNULL);
  1130.             fSecsRecorded = 0;
  1131.         }
  1132.     }
  1133.  
  1134.     // If we don't have a preferred kind yet, use the kind that was found, 
  1135.     // if any.  Otherwise, use our default kind.
  1136.     if ( fPreferredKind == kODNullTypeToken )
  1137.         fPreferredKind = (kindToUse != kODNullTypeToken) ? kindToUse 
  1138.                                                          : gGlobals->fNativeData;
  1139. }
  1140.  
  1141. //------------------------------------------------------------------------------
  1142. // Method:        CloneInto
  1143. // Origin:        ODPart
  1144. //
  1145. // Description:    This method is called during cloning, typically during data
  1146. //                interchange operations (e.g. Cut/Paste). The part should
  1147. //                write out its current state and content.
  1148. //
  1149. // Parent:        The part's parent class was called before this method was
  1150. //                dispatched to (see som_SamplePart.cpp).
  1151. //------------------------------------------------------------------------------
  1152.  
  1153. void SoundEditor::CloneInto( Environment*        ev,
  1154.                              ODDraftKey            key,
  1155.                              ODStorageUnit*        destinationSU,
  1156.                              ODFrame*            initiatingFrame )
  1157. {
  1158.     SOM_Trace("SoundEditor","CloneInto");
  1159.     
  1160.     // We must first verify that we've never written to this storage unit.
  1161.     // If we have, we should do nothing, otherwise we need to write out
  1162.     // the current state of the part content.
  1163.     
  1164.     if ( !destinationSU->Exists(ev, kODPropContents, kSoundEditorKind, 0)
  1165.       && !destinationSU->Exists(ev, kODPropContents, gGlobals->fSndDataKind, 0) )    
  1166.     {
  1167.         // Add the properties we need to successfully externalize
  1168.         // ourselves into the destination storage unit.
  1169.         this->CheckAndAddProperties(ev, destinationSU);
  1170.                 
  1171.         // Write out the part's state information.
  1172.         this->ExternalizeStateInfo(ev, destinationSU, key, initiatingFrame);
  1173.             
  1174.         // Write out the part's content annotation.
  1175.         this->ExternalizeAnnotation(ev, destinationSU, key, initiatingFrame);
  1176.  
  1177.         // Write out the part's content.
  1178.         this->ExternalizeContent(ev, destinationSU, key, initiatingFrame, 
  1179.                                  fPreferredKind);
  1180.     }
  1181. }
  1182.  
  1183. //------------------------------------------------------------------------------
  1184. // Method:        Externalize
  1185. // Origin:        ODPart
  1186. //
  1187. // Description:    This method is called when the user saves the document. The part
  1188. //                should write out its state and content if changes have occurred
  1189. //                and if our storage is writeable.
  1190. //
  1191. // Parent:        The part's parent class was called before this method was
  1192. //                dispatched to (see som_SamplePart.cpp).
  1193. //------------------------------------------------------------------------------
  1194.  
  1195. void SoundEditor::Externalize( Environment* ev )
  1196. {
  1197.     SOM_Trace("SoundEditor","Externalize");
  1198.     
  1199.     TRY
  1200.         if ( fDirty && !fReadOnlyStorage )
  1201.         {
  1202.             // Get our storage unit.
  1203.             ODStorageUnit* storageUnit = fSelf->GetStorageUnit(ev);
  1204.     
  1205.             // Verify that the storage unit has the appropriate properties
  1206.             // and values to allow us to run. If not, add them.
  1207.             this->CheckAndAddProperties(ev, storageUnit);
  1208.         
  1209.             // Verify that there are no "bogus" values in the Content
  1210.             // property.
  1211.             this->CleanseContentProperty(ev, storageUnit);
  1212.         
  1213.             // Write out the part's state information.
  1214.             this->ExternalizeStateInfo(ev, storageUnit, kODNULLKey, kODNULL);
  1215.                 
  1216.             // Write out the part's content annotation.
  1217.             this->ExternalizeAnnotation(ev, storageUnit, kODNULLKey, kODNULL);
  1218.             
  1219.             // Write out the part's content.
  1220.             this->ExternalizeContent(ev, storageUnit, kODNULLKey, kODNULL,
  1221.                                      fPreferredKind);
  1222.             
  1223.             // Flag our part as no longer being dirty.
  1224.             fDirty = kODFalse;
  1225.         }
  1226.     CATCH_ALL
  1227.         // Alert the user of the problem.
  1228.         this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrExternalizeFailed);
  1229.         // Change the exception value, so the DocShell doesn't display an
  1230.         // error dialog.
  1231.         SetErrorCode(kODErrAlreadyNotified);
  1232.         // Alert the caller.
  1233.         RERAISE;
  1234.     ENDTRY
  1235. }
  1236.  
  1237. //------------------------------------------------------------------------------
  1238. // Method:        ExternalizeKinds
  1239. // Origin:        ODPart
  1240. //
  1241. // Description:    This method is called by OpenDoc or the containing part when 
  1242. //                either require the part to write out one or more of the editors
  1243. //                supported content formats.
  1244. //
  1245. //                The part is responsible for writing out the kinds it supports
  1246. //                from the list in its fidelity order (the list will probably not
  1247. //                be in the right order).
  1248. //------------------------------------------------------------------------------
  1249.  
  1250. void SoundEditor::ExternalizeKinds( Environment*    ev,
  1251.                                     ODTypeList*        kindset )
  1252. {
  1253.     SOM_Trace("SoundEditor","ExternalizeKinds");
  1254.  
  1255.     if ( !fReadOnlyStorage )
  1256.     {
  1257.         ODBoolean preferredKindWritten = kODFalse;
  1258.         
  1259.         // Get our storage unit.
  1260.         ODStorageUnit* storageUnit = fSelf->GetStorageUnit(ev);
  1261.         
  1262.         // Verify that the storage unit has the appropriate properties
  1263.         // and values to allow us to run. If not, add them.
  1264.         this->CheckAndAddProperties(ev, storageUnit);
  1265.     
  1266.         // Verify that there are no "bogus" values in the Content
  1267.         // property.
  1268.         this->CleanseContentProperty(ev, storageUnit);
  1269.         
  1270.         // Write out each of the kinds that we support from the list.
  1271.         // Note: We should externalize in fidelity order, but since the
  1272.         // content of SoundEditor's types is about the same, we don't 
  1273.         // check the order.
  1274.         for ( TempODTypeListIterator kind(ev, kindset) ;
  1275.               kind.IsNotComplete() ; kind.Next() )
  1276.         {
  1277.             ODTypeToken kindToken = fSession->Tokenize(ev, kind);
  1278.             
  1279.             if ( IsKindSupported(ev, kindToken) )
  1280.             {
  1281.                 // Write out the part's content in the prescribed format.
  1282.                 this->ExternalizeContent(ev, storageUnit, kODNULLKey, kODNULL, kindToken);
  1283.                 
  1284.                 if ( kindToken == fPreferredKind )
  1285.                     preferredKindWritten = kODTrue;
  1286.             }
  1287.         }
  1288.  
  1289.         // Even if the kind set contains no types we support, we must at least
  1290.         // write out our current "preferred" kind.
  1291.         if ( !preferredKindWritten )
  1292.         {
  1293.             // Write out the part's preferred content kind, which, for SamplePart,
  1294.             // is the only kind.
  1295.             this->ExternalizeContent(ev, storageUnit, kODNULLKey, kODNULL, fPreferredKind);
  1296.         }
  1297.  
  1298.         // Write out the part's content annoation.
  1299.         this->ExternalizeAnnotation(ev, storageUnit, kODNULLKey, kODNULL);
  1300.  
  1301.         // Write out the part's state information.
  1302.         this->ExternalizeStateInfo(ev, storageUnit, kODNULLKey, kODNULL);
  1303.     }
  1304. }
  1305.  
  1306. //------------------------------------------------------------------------------
  1307. // Method:        ChangeKind
  1308. // Origin:        ODPart
  1309. //
  1310. // Description:    This method is called in response to the user
  1311. //                selecting a different item in the Kind popup in the
  1312. //                part/documet info dialog. This signals to the part
  1313. //                that the user wishes the default format of the data
  1314. //                written out to be changed.
  1315. //
  1316. //                The editor should record the new "preferred" kind and change the
  1317. //                UI, if necessary, to allow editing of that kind. Don't write the
  1318. //                properties/values/data until Externalize is called.
  1319. //------------------------------------------------------------------------------
  1320.  
  1321. void SoundEditor::ChangeKind( Environment*        ev,
  1322.                               ODType            kind )
  1323. {
  1324.     SOM_Trace("SoundEditor","ChangeKind");
  1325.  
  1326.     ODTypeToken kindToken = fSession->Tokenize(ev, kind);
  1327.     
  1328.     // Make sure we support the kind we are being asked to use.
  1329.  
  1330.     // First, if the kind we were asked to use is the "file kind", then
  1331.     // use the data kind instead.  The file kind is intended to only
  1332.     // refer to incoming data in a drag-and-drop (or maybe some other
  1333.     // file-based binding) and be paired with an HFS kind with the 
  1334.     // file specification.  However, OpenDoc 1.0 shows this in its
  1335.     // Kind popup menu, and there is no way to prevent it from appearing
  1336.     // there.
  1337.     if ( kindToken == gGlobals->fMacSndFile )
  1338.         kindToken = gGlobals->fMacSndData;    
  1339.     else if ( !IsKindSupported(ev, kindToken) )
  1340.         THROW(kODErrInvalidValueType);
  1341.     
  1342.     // Get our storage unit.
  1343.     ODStorageUnit* storageUnit = fSelf->GetStorageUnit(ev);
  1344.     
  1345.     // We need to make sure the "Preferred Kind" property has the correct 
  1346.     // value right away so that OpenDoc knows our preferred kind has changed.
  1347.     if ( fPreferredKind != kindToken )
  1348.     {
  1349.         fPreferredKind = kindToken;
  1350.         
  1351.         // Write out the user's preferred kind.
  1352.         SetPreferredKind(ev, storageUnit, GetKindFromToken(ev, kindToken));
  1353.             
  1354.         // Changing our kind dirties our content.
  1355.         this->SetDirty(ev);
  1356.         
  1357.         // Immediately externalize ourselves in the "new" format.
  1358.         this->Externalize(ev);
  1359.     }
  1360. }
  1361.  
  1362. //------------------------------------------------------------------------------
  1363. // Method:        ExternalizeStateInfo
  1364. // Origin:        SoundEditor
  1365. //
  1366. // Description:    This method is called during externalization of the part. The
  1367. //                current "state" of the part should be written out. This "state"
  1368. //                information may be lost during Data Interchange operations, so
  1369. //                the part needs to recover gracefully if information is missing
  1370. //                or incomplete.
  1371. //
  1372. // Note:        The function StorageUnitSetValue is a macro which simplifies
  1373. //                the use of ODByteArrary, which is required the StorageUnit
  1374. //                interface. Look in StorUtil.h/cpp for an example of using the
  1375. //                ODByteArray struct.
  1376. //------------------------------------------------------------------------------
  1377.  
  1378. void SoundEditor::ExternalizeStateInfo( Environment*    ev,
  1379.                                         ODStorageUnit*    storageUnit,
  1380.                                         ODDraftKey        key,
  1381.                                         ODFrame*        scopeFrame )
  1382. {
  1383.     SOM_Trace("SoundEditor","ExternalizeStateInfo");
  1384.  
  1385.     ODID        scopeFrameID = (scopeFrame ? scopeFrame->GetID(ev) : kODNULLID);
  1386.     ODDraft*    fromDraft = ODGetDraft(ev,fSelf);
  1387.     
  1388.     // Set up the preferred kind property with our preferred kind.
  1389.     
  1390.     WASSERT(IsKindSupported(ev, fPreferredKind));
  1391.     SetPreferredKind(ev, storageUnit, GetKindFromToken(ev, fPreferredKind));
  1392.     
  1393.     // Externalize the part's display frame list.
  1394.  
  1395.     storageUnit->Focus(ev, kODPropDisplayFrames, kODPosUndefined,
  1396.                        kODWeakStorageUnitRefs, 0, kODPosUndefined);
  1397.     
  1398.     // Persistent object references are stored in a side table, rather than
  1399.     // in the property/value stream. Thus, deleting the contents of a value
  1400.     // will not "delete" the references previously written to that value. To
  1401.     // completely "delete" all references written to the value, we must
  1402.     // remove the value and add it back.
  1403.     storageUnit->Remove(ev);
  1404.     storageUnit->AddValue(ev, kODWeakStorageUnitRefs);
  1405.  
  1406.     CListIterator fiter(fDisplayFrames);
  1407.     for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
  1408.             fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
  1409.     {
  1410.         // Get the ID of the frame we are going to weakly reference.
  1411.         ODID frameID = proxy->GetID();
  1412.         
  1413.         // If a draft key exists, then we are being cloned to another draft.
  1414.         // We must "weak" clone our display frame and reference the cloned
  1415.         // frame. The part re-uses the frameID variable so there aren't two
  1416.         // different GetWeakStorageUnitRef calls.
  1417.         if ( key )
  1418.             frameID = fromDraft->WeakClone(ev, key, frameID, kODNULLID, scopeFrameID);
  1419.         
  1420.         // Write out weak references to each of the part's display frames.
  1421.         ODStorageUnitRef weakRef;
  1422.         storageUnit->GetWeakStorageUnitRef(ev, frameID, weakRef);
  1423.         TRY
  1424.             StorageUnitSetValue(storageUnit, ev, kODStorageUnitRefSize, (ODPtr)&weakRef);
  1425.         CATCH_ALL
  1426.             // consume the exception
  1427.         ENDTRY
  1428.     }
  1429. }
  1430.  
  1431. //------------------------------------------------------------------------------
  1432. // Method:        ExternalizeAnnotation
  1433. // Origin:        SoundEditor
  1434. //
  1435. // Description:    This method is called during externalization of the part. The
  1436. //                annotation consists of values which are stored in the content 
  1437. //                but need to be readily accessible.
  1438. //------------------------------------------------------------------------------
  1439.  
  1440. void SoundEditor::ExternalizeAnnotation( Environment*        ev,
  1441.                                             ODStorageUnit*        storageUnit,
  1442.                                             ODDraftKey            key,
  1443.                                             ODFrame*            scopeFrame )
  1444. {
  1445.     SOM_Trace("SoundEditor","ExternalizeAnnotation");
  1446.     
  1447.     // Externalize the default recording quality.
  1448.     storageUnit->Focus(ev, kPropRecordingQuality, kODPosUndefined,
  1449.                        kOSType, 0, kODPosUndefined);
  1450.     
  1451.     OSType rQuality = fRecorder->GetQuality();
  1452.     StorageUnitSetValue(storageUnit, ev, sizeof(OSType), &rQuality);
  1453.  
  1454.     // Externalize the recorded sound length.
  1455.     ODSetULongProp(ev, storageUnit, kPropSecsRecorded, kODULong, fSecsRecorded);
  1456.         // Here we use a routine from StdTypIO.h, an OpenDoc Utility to simplify
  1457.         // outputting standard data types.  This routine also will perform endian-
  1458.         // swapping to put the value in the right form to be standard.
  1459. }
  1460.  
  1461. //------------------------------------------------------------------------------
  1462. // Method:        ExternalizeContent
  1463. // Origin:        SoundEditor
  1464. //
  1465. // Description:    This method is called during exteralization of the part. The
  1466. //                content of the part should be written out.
  1467. //
  1468. //                The part has no intrinsic content, so the part does nothing.
  1469. //                The method is here for completeness.
  1470. //------------------------------------------------------------------------------
  1471.  
  1472. void SoundEditor::ExternalizeContent( Environment*        ev,
  1473.                                       ODStorageUnit*    storageUnit,
  1474.                                       ODDraftKey        key,
  1475.                                       ODFrame*            scopeFrame,
  1476.                                       ODTypeToken        kindToken )
  1477. {
  1478.     SOM_Trace("SoundEditor","ExternalizeContent");
  1479.  
  1480.     // Focus to the preferred kind for the part.
  1481.     
  1482.     storageUnit->Focus(ev, kODPropContents, kODPosUndefined,
  1483.                        GetKindFromToken(ev, kindToken), 0, kODPosUndefined);
  1484.     
  1485.     if ( fPlayer->GetSound() )
  1486.     {
  1487.         // Write out the data.
  1488.         
  1489.         ODULong oldSize = storageUnit->GetSize(ev);
  1490.         ODULong soundSize = ODGetHandleSize(fPlayer->GetSound());
  1491.         TempHandleLockAndRememberState lock(fPlayer->GetSound());
  1492.         ODValue soundDataPtr = *lock;
  1493.         
  1494.         StorageUnitSetValue(storageUnit, ev, soundSize, soundDataPtr);
  1495.  
  1496.         if ( oldSize > soundSize )
  1497.             storageUnit->DeleteValue(ev, oldSize - soundSize);
  1498.     }
  1499.     else
  1500.     {
  1501.         ODULong oldSize = storageUnit->GetSize(ev);
  1502.         if ( oldSize > 0 )
  1503.             storageUnit->DeleteValue(ev, oldSize);
  1504.     }
  1505. }
  1506.  
  1507. //------------------------------------------------------------------------------
  1508. // Method:        CleanseContentProperty
  1509. // Origin:        SoundEditor
  1510. //
  1511. // Description:    This method is called during externalization of the part so 
  1512. //                that the part can remove any value in the content property
  1513. //                that it cannot "accurately" write to.
  1514. //
  1515. // Note:        "Additional" values will be added to a part's content property
  1516. //                during Drag & Drop operations.
  1517. //------------------------------------------------------------------------------
  1518.  
  1519. void SoundEditor::CleanseContentProperty( Environment*        ev,
  1520.                                           ODStorageUnit*    storageUnit )
  1521. {
  1522.     SOM_Trace("SoundEditor","CleanseContentProperty");
  1523.  
  1524.     storageUnit->Focus(ev, kODPropContents, kODPosUndefined, kODNULL, 0, 
  1525.                        kODPosAll);
  1526.     
  1527.     ODULong numValues = storageUnit->CountValues(ev);
  1528.     
  1529.     for (ODULong index = numValues; index >= 1; index--)
  1530.     {
  1531.         // Index from 1 to n through the values.
  1532.         storageUnit->Focus(ev, kODPropContents, kODPosUndefined, kODNULL, 
  1533.                            index, kODPosUndefined);
  1534.         TempODValueType value = storageUnit->GetType(ev);
  1535.         
  1536. #if 0
  1537.         // If the value type is not one we support, remove it.
  1538.         if ( !ODISOStrEqual(value, kSoundEditorKind) &&
  1539.              !ODISOStrEqual(value, gGlobals->fSndDataKind) )
  1540.         {
  1541.             storageUnit->Remove(ev);
  1542.         }
  1543. #else                    
  1544.         ODBoolean remove = kODTrue;
  1545.  
  1546.         if ( (fPreferredKind == gGlobals->fNativeData || fPreferredKind == kODNullTypeToken) 
  1547.              && ODISOStrEqual(value, kSoundEditorKind) )
  1548.             remove = kODFalse;
  1549.         
  1550.         if ( ODISOStrEqual(value, gGlobals->fSndDataKind) )
  1551.             remove = kODFalse;
  1552.  
  1553.         if (remove)
  1554.             storageUnit->Remove(ev);
  1555. #endif
  1556.     }
  1557. }
  1558.  
  1559. //------------------------------------------------------------------------------
  1560. // Method:        CheckAndAddProperties
  1561. // Origin:        SoundEditor
  1562. //
  1563. // Description:    This method is called during externalization of the part to
  1564. //                verify that all the properties needed to persistently represent
  1565. //                the current running state of the part exist.
  1566. //
  1567. //                The part adds the default content property, a preferred editor
  1568. //                property (to aid in part binding), and a display frames property.
  1569. //
  1570. // Note:        The function StorageUnitSetValue is a macro which simplifies
  1571. //                the use of ODByteArrary, which is required the StorageUnit
  1572. //                interface. Look in StorUtil.h/cpp for an example of using the
  1573. //                ODByteArray struct.
  1574. //------------------------------------------------------------------------------
  1575.  
  1576. void SoundEditor::CheckAndAddProperties( Environment*    ev,
  1577.                                          ODStorageUnit*    storageUnit )
  1578. {
  1579.     SOM_Trace("SoundEditor","CheckAndAddProperties");
  1580.  
  1581.     // Create our content property and preferred content property kind.
  1582.  
  1583.     if ( !storageUnit->Exists(ev, kODPropContents, kODNULL, 0) )
  1584.         storageUnit->AddProperty(ev, kODPropContents);
  1585.     if ( !storageUnit->Exists(ev, kODPropContents, kSoundEditorKind, 0) )
  1586.     {
  1587.         storageUnit->Focus(ev, kODPropContents, kODPosUndefined, kODNULL, 0, kODPosUndefined);
  1588.         storageUnit->AddValue(ev, kSoundEditorKind);
  1589.     }
  1590.     if ( !storageUnit->Exists(ev, kODPropContents, gGlobals->fSndDataKind, 0) )
  1591.     {
  1592.         storageUnit->Focus(ev, kODPropContents, kODPosUndefined, kODNULL, 0, kODPosUndefined);
  1593.         storageUnit->AddValue(ev, gGlobals->fSndDataKind);
  1594.     }
  1595.  
  1596.     // Since we are setting up the preferred kind property, we just write
  1597.     // out our default "kind" for the editor. We can write out the user
  1598.     // chosen kind in the ExternalizeStateInfo method.
  1599.  
  1600.     if ( !storageUnit->Exists(ev, kODPropPreferredKind, kODISOStr, 0) )
  1601.         SetPreferredKind(ev, storageUnit, kSoundEditorKind);
  1602.     
  1603.     // Add our content annotation for recording quality.
  1604.  
  1605.     if ( !storageUnit->Exists(ev, kPropRecordingQuality, kOSType, 0) )
  1606.         storageUnit->AddProperty(ev, kPropRecordingQuality)->AddValue(ev, kOSType);
  1607.  
  1608.     // Add our content annotation for recorded sound length.
  1609.     
  1610.     if ( !storageUnit->Exists(ev, kPropSecsRecorded, kODULong, 0) )
  1611.         storageUnit->AddProperty(ev, kPropSecsRecorded)->AddValue(ev, kODULong);
  1612.  
  1613.     // Add our display frame list.
  1614.     
  1615.     if ( !storageUnit->Exists(ev, kODPropDisplayFrames, kODNULL, 0) )
  1616.         storageUnit->AddProperty(ev, kODPropDisplayFrames);
  1617.     if ( !storageUnit->Exists(ev, kODPropDisplayFrames, kODWeakStorageUnitRefs, 0) )
  1618.     {
  1619.         storageUnit->Focus(ev, kODPropDisplayFrames, kODPosUndefined, kODNULL, 0, kODPosAll);
  1620.         storageUnit->AddValue(ev, kODWeakStorageUnitRefs);
  1621.     }
  1622. }
  1623.  
  1624. //------------------------------------------------------------------------------
  1625. // Method:        SetDirty
  1626. // Origin:        SoundEditor
  1627. //
  1628. // Description:    This method is called by the part when the content or state of
  1629. //                the part has been modified by the user and the "Save" menu item
  1630. //                should be enabled.
  1631. //------------------------------------------------------------------------------
  1632.  
  1633. void SoundEditor::SetDirty( Environment*    ev )
  1634. {
  1635.     SOM_Trace("SoundEditor","SetDirty");
  1636.  
  1637.     // There is no need to repeatedly tell the draft we have changed;
  1638.     // once is sufficient.
  1639.     if ( !fDirty && !fReadOnlyStorage )
  1640.     {
  1641.         fDirty = kODTrue;
  1642.         ODGetDraft(ev,fSelf)->SetChangedFromPrev(ev);
  1643.     }
  1644. }
  1645.  
  1646. //------------------------------------------------------------------------------
  1647. // Method:        ReadPartInfo
  1648. // Origin:        ODPart
  1649. //
  1650. // Description:    When a frame is being internalized by the Draft, it will ask the
  1651. //                owner (part) to read in its info annotation on the frame.
  1652. //
  1653. //                The part uses a C++ helper class to encapsulate the information
  1654. //                we store with each frame, so we let it internalize itself from
  1655. //                the storage unit view.
  1656. //------------------------------------------------------------------------------
  1657.  
  1658. ODInfoType SoundEditor::ReadPartInfo( Environment*            ev,
  1659.                                       ODFrame*                frame,
  1660.                                       ODStorageUnitView*    storageUnitView )
  1661. {
  1662.     SOM_Trace("SoundEditor","ReadPartInfo");
  1663.  
  1664.     CFrameInfo* frameInfo = new CFrameInfo(fSession);
  1665.     
  1666.     TRY
  1667.         frameInfo->InitFromStorage(ev, storageUnitView);
  1668.     CATCH_ALL
  1669.         // Clean up the allocated memory.
  1670.         ODDeleteObject(frameInfo);
  1671.         // Alert the caller.
  1672.         RERAISE;
  1673.     ENDTRY
  1674.     
  1675.     return (ODInfoType) frameInfo;
  1676. }
  1677.  
  1678. //------------------------------------------------------------------------------
  1679. // Method:        WritePartInfo
  1680. // Origin:        ODPart
  1681. //
  1682. // Description:    When a frame is being externalized by the Draft, it will ask the
  1683. //                owner (part) to write out its info annotation on the frame.
  1684. //
  1685. //                The part uses a C++ helper class to encapsulate the information
  1686. //                we store with each frame, so we let it externalize itself to
  1687. //                the storage unit view.
  1688. //------------------------------------------------------------------------------
  1689.  
  1690. void SoundEditor::WritePartInfo( Environment*        ev,
  1691.                                    ODInfoType            partInfo,
  1692.                                  ODStorageUnitView*    storageUnitView )
  1693. {
  1694.     SOM_Trace("SoundEditor","WritePartInfo");
  1695.  
  1696.     // Tell our frame info class to write itself out into the pre-
  1697.     // focused storage unit.
  1698.     ((CFrameInfo*) partInfo)->Externalize(ev, storageUnitView);
  1699. }
  1700.  
  1701. //------------------------------------------------------------------------------
  1702. // Method:        ClonePartInfo
  1703. // Origin:        ODPart
  1704. //
  1705. // Description:    When a frame is being cloned by the Draft, it will ask the owner
  1706. //                (part) to clone its info annotation on the frame.
  1707. //
  1708. //                The part uses a C++ helper class to encapsulate the information
  1709. //                we store with each frame, so we let it clone itself to the
  1710. //                storage unit view.
  1711. //------------------------------------------------------------------------------
  1712.  
  1713. void SoundEditor::ClonePartInfo( Environment*        ev,
  1714.                                  ODDraftKey            key,
  1715.                                    ODInfoType            partInfo,
  1716.                                  ODStorageUnitView*    storageUnitView,
  1717.                                  ODFrame*            scopeFrame )
  1718. {
  1719.     SOM_Trace("SoundEditor","ClonePartInfo");
  1720.  
  1721.     // Tell our frame info class to write itself out into the pre-
  1722.     // focused storage unit.
  1723.     ((CFrameInfo*) partInfo)->CloneInto(ev, key, storageUnitView, scopeFrame);
  1724. }
  1725.  
  1726. //==============================================================================
  1727. #pragma mark    • Layout •
  1728. //==============================================================================
  1729.  
  1730. //------------------------------------------------------------------------------
  1731. // Method:        DisplayFrameAdded
  1732. // Origin:        ODPart
  1733. //
  1734. // Description:    This method is called in response to a frame being created for
  1735. //                the part.
  1736. //
  1737. //                The part records the existence of a new display frame in its
  1738. //                internal display frame list, as well as, verify that the frame
  1739. //                is "set up" correctly (i.e., valid viewType). The part also
  1740. //                creates and stores its "frame info" class in the new frame. 
  1741. //------------------------------------------------------------------------------
  1742.  
  1743. void SoundEditor::DisplayFrameAdded( Environment*    ev,
  1744.                                         ODFrame*        frame )
  1745. {
  1746.     SOM_Trace("SoundEditor","DisplayFrameAdded");
  1747.  
  1748.     // If we are being embedded in another part, the presentation field
  1749.     // will (most likely) be unset; we need to set it something meaningful.
  1750.     // The view field may also be unset, if so, we prefer to be displayed
  1751.     // in a frame view.
  1752.  
  1753.     if ( frame->GetPresentation(ev) != gGlobals->fControlsPresentation )
  1754.         frame->SetPresentation(ev, gGlobals->fControlsPresentation);
  1755.     
  1756.     if ( frame->GetViewType(ev) == kODNullTypeToken )
  1757.         frame->SetViewType(ev, gGlobals->fFrameView);
  1758.         
  1759.     // Hang our "state" info off of the new display frame. We use
  1760.     // the CFrameInfo object for activation, updating, and window
  1761.     // maintenance.
  1762.  
  1763.     CFrameInfo* frameInfo = new CFrameInfo(fSession);
  1764.     frame->SetPartInfo(ev, (ODInfoType) frameInfo);
  1765.         
  1766.     // If the frame being added is a root frame, we know that a window
  1767.     // is associated with this frame. Notify ourselves that we need to 
  1768.     // clean it up when the frame goes away.
  1769.     if ( frame->IsRoot(ev) )
  1770.         frameInfo->SetShouldDisposeWindow(kODTrue);
  1771.     
  1772.     // The proxy class will refcount the frame passed to it, so we
  1773.     // don't need to worry about refcounting the display frame.
  1774.     CFrameProxy* proxy = new CFrameProxy;
  1775.     proxy->InitFrameProxy(ev, frame);
  1776.  
  1777.     // Add the proxy to the display frame collection.
  1778.     fDisplayFrames->Add(proxy);
  1779.  
  1780.     // Since we maintain a persistent list of weak references to our
  1781.     // display frames, having one added to the part dirties it.
  1782.     this->SetDirty(ev);
  1783. }
  1784.  
  1785. //------------------------------------------------------------------------------
  1786. // Method:        DisplayFrameConnected
  1787. // Origin:        ODPart
  1788. //
  1789. // Description:    This method is called when one of our display frames, previously
  1790. //                written out, is internalized. This method is called instead of
  1791. //                DisplayFrameAdded because a "new" frame is not being created;
  1792. //                an existing one is being reconstituted.
  1793. //
  1794. //                The part has nothing special to do, so we just add it to the
  1795. //                part's internal display frame list.
  1796. //------------------------------------------------------------------------------
  1797.  
  1798. void SoundEditor::DisplayFrameConnected( Environment*    ev,
  1799.                                          ODFrame*        frame )
  1800. {
  1801.     SOM_Trace("SoundEditor","DisplayFrameConnected");
  1802.  
  1803.     // Iterate over our display collection to match the frame with
  1804.     // an existing proxy with the correct frame ID. If we find it,
  1805.     // replace the ID with the actual frame. If we don't find it,
  1806.     // treat it as a newly "added" frame.
  1807.     ODBoolean found = kODFalse;
  1808.     CListIterator fiter(fDisplayFrames);
  1809.     for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
  1810.             fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
  1811.     {
  1812.         if ( proxy->GetID() == frame->GetID(ev) )
  1813.         {
  1814.             // The proxy class will refcount the frame passed to it, so we
  1815.             // don't need to worry about refcounting the display frame.
  1816.             proxy->SetFrame(ev, frame);
  1817.             found = kODTrue;
  1818.         }
  1819.     }
  1820.  
  1821.     // Parts typically operate under the assumption that they have previous
  1822.     // knowledge of a frame before it is connected to it. This knowledge
  1823.     // should come from having read in the frame reference when the part
  1824.     // was internalized. If the frame is an "unknown", the part was probably
  1825.     // bound to another editors storage unit because the editor is missing or
  1826.     // the user changed the editor in the Info dialog. 
  1827.     if ( found )
  1828.     {
  1829.         // If a display frame is connected to us with an unrecognizable
  1830.         // presentation, we need to set it to something meaningful.
  1831.         if ( frame->GetPresentation(ev) != gGlobals->fControlsPresentation )
  1832.             frame->SetPresentation(ev, gGlobals->fControlsPresentation);
  1833.         
  1834.         // If the frame being added is a root frame, we know that a window
  1835.         // is associated with this frame. Notify ourselves that we need to 
  1836.         // clean it up when the frame goes away.
  1837.         if ( frame->IsRoot(ev) )
  1838.         {
  1839.             CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  1840.             frameInfo->SetShouldDisposeWindow(kODTrue);
  1841.             
  1842.             // In addition, we need to verify that the frame is in "frame" view.
  1843.             // If the user dragged an icon from a document to Finder and then
  1844.             // opens the resultant document, the view would be "icon". However
  1845.             // displaying a icon is useless, so we need to change the view to
  1846.             // frame.
  1847.             if ( frame->GetViewType(ev) != gGlobals->fFrameView )
  1848.                 frame->SetViewType(ev, gGlobals->fFrameView);    
  1849.         }
  1850.     }
  1851.     else
  1852.     {
  1853.         // If an unrecognizable frame is connected to us, treat it like a "new"
  1854.         // frame and call our method to add it.
  1855.         this->DisplayFrameAdded(ev, frame);
  1856.     }
  1857. }
  1858.  
  1859. //------------------------------------------------------------------------------
  1860. // Method:        DisplayFrameRemoved
  1861. // Origin:        ODPart
  1862. //
  1863. // Description:    This method is called in response to a frame being removed from
  1864. //                the part.
  1865. //
  1866. //                The part removes the frame from its internal display frame list
  1867. //                and reliquishes any foci that it still owned. Lastly, if the
  1868. //                frame has a source frame (it was the root frame of a part
  1869. //                window), we will record the part window bounds so that any
  1870. //                subsequent part windows opened on the source frame will appear
  1871. //                in the same location.
  1872. //------------------------------------------------------------------------------
  1873.  
  1874. void SoundEditor::DisplayFrameRemoved( Environment*        ev,
  1875.                                        ODFrame*            frame )
  1876. {
  1877.     SOM_Trace("SoundEditor","DisplayFrameRemoved");
  1878.  
  1879.     TRY
  1880.         CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  1881.  
  1882.         // Make sure to unregister the "idle" frame if the user
  1883.         // closes the palette window while a sound is playing; otherwise,
  1884.         // a "refcount" error will occur.
  1885.         if ( fPlayer->GetFrame() == frame )
  1886.         {
  1887.             fSession->GetDispatcher(ev)->UnregisterIdle(ev, fSelf, frame);
  1888.             fPlayer->SetFrame(kODNULL);
  1889.         }
  1890.         
  1891.         // Make sure the frame going away does not own any foci. Forgetting
  1892.         // to do this, will cause a "refcounting" error when the frame
  1893.         // is deleted by the draft.
  1894.         this->RelinquishAllFoci(ev, frame);
  1895.         
  1896.         // If removing a child window, show zoom rects back to source.
  1897.         // NOTE: This has to be done before CleanupDisplayFrame because 
  1898.         // CleanupDisplayFrame will remove the relationship.
  1899.         if ( frame->IsRoot(ev) && frameInfo->HasSourceFrame() )
  1900.         {
  1901.             TempODWindow window = frame->AcquireWindow(ev);
  1902.             this->ZoomPartWindow(ev, frameInfo->GetSourceFrame(ev),
  1903.                                  window, kWindowClosing);
  1904.         }
  1905.         
  1906.         // Clean up the display frame.
  1907.         this->CleanupDisplayFrame(ev, frame, kFrameRemoved);
  1908.         
  1909.         // Clean up any associated window.
  1910.         this->CleanupWindow(ev, frame);
  1911.         
  1912.         // Clean up the display frame, remove it from our internal list,
  1913.         // and let go of it.
  1914.         frame->SetPartInfo(ev, (ODInfoType) kODNULL);
  1915.         ODDeleteObject(frameInfo);
  1916.         
  1917.         // Remove the display frame from our collection.
  1918.         CListIterator fiter(fDisplayFrames);
  1919.         for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
  1920.                 fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
  1921.         {
  1922.             if ( ODObjectsAreEqual(ev, proxy->GetFrame(ev), frame) )
  1923.             {
  1924.                 // Delete the proxy object and its contents. The frame's
  1925.                 // refcount will be decremented in the proxy destructor.
  1926.                 fiter.RemoveCurrent();
  1927.                 delete proxy;
  1928.             }
  1929.         }
  1930.  
  1931.         // Since we maintain a persistent list of weak references to our
  1932.         // display frames, having one removed from the part dirties it.
  1933.         this->SetDirty(ev);
  1934.     CATCH_ALL
  1935.         // Alert the user of the problem.
  1936.         this->DoDialogBox(ev, frame, kErrorBoxID, kErrRemoveFrame);
  1937.         // Change the exception value, so the DocShell doesn't display an
  1938.         // error dialog.
  1939.         SetErrorCode(kODErrAlreadyNotified);
  1940.         // Alert the caller.
  1941.         RERAISE;
  1942.     ENDTRY
  1943. }
  1944.  
  1945. //------------------------------------------------------------------------------
  1946. // Method:        DisplayFrameClosed
  1947. // Origin:        ODPart
  1948. //
  1949. // Description:    This method is called in response to a frame being closed as a
  1950. //                result of the document having been closed by the user.
  1951. //
  1952. //                The part behaves much the same way that it would if a frame were
  1953. //                removed (see above), except that we don't need to cache runtime
  1954. //                information. 
  1955. //------------------------------------------------------------------------------
  1956.  
  1957. void SoundEditor::DisplayFrameClosed( Environment*    ev,
  1958.                                       ODFrame*        frame )
  1959. {
  1960.     SOM_Trace("SoundEditor","DisplayFrameClosed");
  1961.  
  1962.     TRY
  1963.         // Make sure to unregister the "idle" frame if the user
  1964.         // closes the palette window while a sound is playing; otherwise,
  1965.         // a "refcount" error will occur.
  1966.         if ( fPlayer->GetFrame() == frame )
  1967.         {
  1968.             fSession->GetDispatcher(ev)->UnregisterIdle(ev, fSelf, frame);
  1969.             fPlayer->SetFrame(kODNULL);
  1970.         }
  1971.         
  1972.         // Make sure the frame going away does own any foci. Forgetting
  1973.         // to do this, will cause a "refcounting" error when the frame
  1974.         // is deleted by the draft.    
  1975.         this->RelinquishAllFoci(ev, frame);
  1976.         
  1977.         // Clean up the display frame.
  1978.         this->CleanupDisplayFrame(ev, frame, kFrameClosed);
  1979.         
  1980.         // Clean up any associated window.
  1981.         this->CleanupWindow(ev, frame);
  1982.  
  1983.         // Clean up the display frame, remove it from our internal list,
  1984.         // and let go of it.
  1985.         CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  1986.         frame->SetPartInfo(ev, (ODInfoType) kODNULL);
  1987.         ODDeleteObject(frameInfo);
  1988.         
  1989.         // Remove the display frame from our collection.
  1990.         CListIterator fiter(fDisplayFrames);
  1991.         for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
  1992.                 fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
  1993.         {
  1994.             if ( proxy->GetID() == frame->GetID(ev) )
  1995.             {
  1996.                 // Release the frame reference, but don't get rid of the
  1997.                 // proxy object because "closed" frames may be reconnected
  1998.                 // before the document is closed.
  1999.                 proxy->Purge(ev);
  2000.             }
  2001.         }
  2002.         
  2003.     CATCH_ALL
  2004.         // Alert the user of the problem.
  2005.         this->DoDialogBox(ev, frame, kErrorBoxID, kErrRemoveFrame);
  2006.         // Change the exception value, so the DocShell doesn't display an
  2007.         // error dialog.
  2008.         SetErrorCode(kODErrAlreadyNotified);
  2009.         // Alert the caller.
  2010.         RERAISE;
  2011.     ENDTRY
  2012. }
  2013.  
  2014. //------------------------------------------------------------------------------
  2015. // Method:        CleanupDisplayFrame
  2016. // Origin:        SoundEditor
  2017. //
  2018. // Description:    This method is called when a frame has been closed or removed.
  2019. //                The method cleans up the references and state information stored
  2020. //                in the CFrameInfo class.
  2021. //------------------------------------------------------------------------------
  2022.  
  2023. void SoundEditor::CleanupDisplayFrame( Environment*    ev,
  2024.                                        ODFrame*        frame,
  2025.                                        ODBoolean    frameRemoved )
  2026. {
  2027.     SOM_Trace("SamplePart","CleanupDisplayFrame");
  2028.  
  2029.     ODError error = noErr;
  2030.     CFrameInfo*    frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  2031.  
  2032.     TRY
  2033.         // If we are the root of a child window, we need to notify
  2034.         // our source frame that we are going away.
  2035.         if ( frameInfo->HasSourceFrame() )
  2036.         {
  2037.             ODFrame* sourceFrame = frameInfo->GetSourceFrame(ev);
  2038.             CFrameInfo* sourceFrameInfo = (CFrameInfo*) sourceFrame->GetPartInfo(ev);
  2039.  
  2040.             if ( frameRemoved )
  2041.             {
  2042.                 // Invalidate the source frame. We do this because the
  2043.                 // source frame may have a unique display when it has
  2044.                 // been opened into a part window. This forces the
  2045.                 // frame to redraw "not opened".
  2046.                 sourceFrame->Invalidate(ev, kODNULL, kODNULL);
  2047.             }
  2048.             
  2049.             // Tell the source frame that its dependent is going away.
  2050.             sourceFrameInfo->ReleaseDependentFrame(ev);            
  2051.  
  2052.             // Release our reference to the source frame.
  2053.             frameInfo->ReleaseSourceFrame(ev);
  2054.             
  2055.             // If the frame is the root, it is a part window going away
  2056.             // and we need to notify our source frame that it no longer
  2057.             // has a part window.
  2058.             if ( frame->IsRoot(ev) )
  2059.                 sourceFrameInfo->SetPartWindow(ev, kODNULL);
  2060.         }
  2061.     CATCH_ALL
  2062.         error = ErrorCode();
  2063.     ENDTRY
  2064.     
  2065.     TRY
  2066.         // If the frame was removed from the document, we need to remove
  2067.         // any child window displaying that frame.
  2068.         if ( frameRemoved )
  2069.         {
  2070.             // If we have a child window, we need to close it.
  2071.             ODWindow* window = frameInfo->AcquirePartWindow(ev);
  2072.             if ( window )
  2073.             {
  2074.                 frameInfo->SetPartWindow(ev, kODNULL);
  2075.                 window->CloseAndRemove(ev);
  2076.             }
  2077.         }
  2078.     CATCH_ALL
  2079.         error = ErrorCode();
  2080.     ENDTRY
  2081.  
  2082.     TRY
  2083.         // If we have dependent frames, we need to notify them that we
  2084.         // are going away.
  2085.     
  2086.         if ( frameInfo->HasDependentFrame() )
  2087.         {
  2088.             // Get the frame that is dependent on this one. We can safely
  2089.             // do this because we only reference our own display frames.
  2090.             ODFrame* dependentFrame = frameInfo->GetDependentFrame(ev);
  2091.             CFrameInfo* dependentFrameInfo = (CFrameInfo*) dependentFrame->GetPartInfo(ev);
  2092.             
  2093.             // Tell the dependent frame that its source is going away.
  2094.             dependentFrameInfo->ReleaseSourceFrame(ev);
  2095.             
  2096.             // Release our reference to the dependent frame.
  2097.             frameInfo->ReleaseDependentFrame(ev);
  2098.         }
  2099.     CATCH_ALL
  2100.         error = ErrorCode();
  2101.     ENDTRY
  2102.     
  2103.     // If anything went wrong, signal an error.
  2104.     THROW_IF_ERROR(error);
  2105. }
  2106.  
  2107. //------------------------------------------------------------------------------
  2108. // Method:        AttachSourceFrame
  2109. // Origin:        ODPart
  2110. //
  2111. // Description:    If the part which we are contained in is opened into a part
  2112. //                window, it is required to iterate over its embedded frames and
  2113. //                add new display frames in the part window. After each new
  2114. //                embedded frame is created, this method will be called.
  2115. //
  2116. //                Given all that, and given our lack of interesting
  2117. //                content, we just validate the frame and attach it to its source.
  2118. //------------------------------------------------------------------------------
  2119.  
  2120. void SoundEditor::AttachSourceFrame( Environment*    ev,
  2121.                                      ODFrame*        frame,
  2122.                                      ODFrame*        sourceFrame )
  2123. {
  2124.  
  2125.     SOM_Trace("SoundEditor","AttachSourceFrame");
  2126.  
  2127.     // Tell the new frame about its source.
  2128.     CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  2129.     frameInfo->SetSourceFrame(ev, sourceFrame);
  2130.     
  2131.     // And tell the source about its new dependent.
  2132.     frameInfo = (CFrameInfo*) sourceFrame->GetPartInfo(ev);
  2133.     frameInfo->SetDependentFrame(ev, frame);
  2134.     
  2135.     // In both cases, refcounting of the frame and sourceFrame is
  2136.     // handled by the CFrameInfo class.
  2137. }
  2138.  
  2139. //------------------------------------------------------------------------------
  2140. // Method:        ViewTypeChanged
  2141. // Origin:        ODPart
  2142. //
  2143. // Description:    This method is called in response to one of our display frame's
  2144. //                viewType field being modified. We call this method on ourselves
  2145. //                when new display frames are added, but it call also be called
  2146. //                when the user changes the view in the "part info" dialog.
  2147. //
  2148. //                The part first loads the appropriate view icons if needed and
  2149. //                then calculates a new "used" shape based on the bounds of the
  2150. //                new view type. If any problems occur while changing the view,
  2151. //                the part defaults back to frame view (the part's default view).
  2152. //------------------------------------------------------------------------------
  2153.  
  2154. void
  2155. SoundEditor::ViewTypeChanged( Environment*    ev,
  2156.                               ODFrame*        frame )
  2157. {
  2158.     SOM_Trace("SoundEditor","ViewTypeChanged");
  2159.  
  2160.     // Change this frame's used shape to match the new view setting.
  2161.     TempODShape newUsedShape = this->CalcNewUsedShape(ev, frame);    
  2162.  
  2163.     frame->Invalidate(ev, kODNULL, kODNULL);
  2164.     frame->ChangeUsedShape(ev, newUsedShape, kODNULL);
  2165.     frame->Invalidate(ev, kODNULL, kODNULL);
  2166. }
  2167.  
  2168. //------------------------------------------------------------------------------
  2169. // Method:        GenerateThumbnail
  2170. // Origin:        SoundEditor
  2171. //
  2172. // Description:    This method is called by the part to generate a thumbnail view
  2173. //                from the current content.
  2174. //
  2175. //                The part has no visible content, so we merely load a picture.
  2176. //------------------------------------------------------------------------------
  2177.  
  2178. PicHandle SoundEditor::GenerateThumbnail( Environment*    ev,
  2179.                                           ODFrame*        frame )
  2180. {
  2181.     SOM_Trace("SoundEditor","GenerateThumbnail");
  2182.     
  2183.     if ( gGlobals->fThumbnail == kODNULL )
  2184.     {
  2185.         // In cases where a part has been instantiated from scratch and has no
  2186.         // content (yet), it is appropriate to display a PICT or some graphic
  2187.         // in place of a "real" thumbnail.
  2188.         LoadThumbnail(ev, &gGlobals->fThumbnail);
  2189.         
  2190.         // If we were unable to load the PICT resource for whatever reason
  2191.         // we will default back to a "frame" view and throw the Resource
  2192.         // Manager error as an exception.
  2193.         if ( gGlobals->fThumbnail == kODNULL )
  2194.         {
  2195.             frame->ChangeViewType(ev, gGlobals->fFrameView);
  2196.             
  2197.             // There is a bug in ResError, when resources are not found,
  2198.             // which may cause noErr to be returned. If that is the case,
  2199.             // we throw resNotFound.
  2200.             THROW_IF_ERROR(ResError());
  2201.             THROW(resNotFound);
  2202.         }
  2203.     }
  2204.         
  2205.     return (PicHandle) gGlobals->fThumbnail;
  2206. }
  2207.  
  2208. //------------------------------------------------------------------------------
  2209. // Method:        LoadControls
  2210. // Origin:        SoundEditor
  2211. //
  2212. // Description:    This method is called by the part to load its control palette.
  2213. //------------------------------------------------------------------------------
  2214.  
  2215. PicHandle SoundEditor::LoadControls( Environment*    ev,
  2216.                                      ODFrame*        frame )
  2217. {
  2218.     SOM_Trace("SoundEditor","LoadControls");
  2219.     
  2220.     if ( gGlobals->fControlsPict == kODNULL )
  2221.     {
  2222.         TRY
  2223.             CUsingLibraryResources res;
  2224.             
  2225.             gGlobals->fControlsPict = (Handle) GetPicture(kControlsPICT);
  2226.             
  2227.             if ( gGlobals->fControlsPict == kODNULL )
  2228.             {    
  2229.                 // There is a bug in ResError, when resources are not found,
  2230.                 // which may cause noErr to be returned. If that is the case,
  2231.                 // we throw resNotFound.
  2232.                 THROW_IF_ERROR(ResError());
  2233.                 THROW(resNotFound);
  2234.             }
  2235.             
  2236.             DetachResource(gGlobals->fControlsPict);
  2237.  
  2238.         CATCH_ALL
  2239.             // If we were unable to load the PICT resource for whatever reason
  2240.             // we will default back to a "large icon" view and throw the Resource
  2241.             // Manager error as an exception.
  2242.             frame->ChangeViewType(ev, gGlobals->fLargeIconView);
  2243.             RERAISE;
  2244.         ENDTRY
  2245.     }
  2246.     
  2247.     return (PicHandle) gGlobals->fControlsPict;
  2248. }
  2249.  
  2250. //------------------------------------------------------------------------------
  2251. // Method:        FrameShapeChanged
  2252. // Origin:        ODPart
  2253. //
  2254. // Description:    This method is called in response to a frame's shape being
  2255. //                altered, either by the user or the part we are embedded in.
  2256. //
  2257. //                To keep all dependent frames in sync, we need to propogate the
  2258. //                new frame shape the frames dependent on the changed frame. This
  2259. //                is done by observing the display frames stored in the frame info
  2260. //                and calling RequestFrameShape for each.
  2261. //------------------------------------------------------------------------------
  2262.  
  2263. void SoundEditor::FrameShapeChanged( Environment*    ev,
  2264.                                      ODFrame*        frame )
  2265. {
  2266.     SOM_Trace("SoundEditor","FrameShapeChanged");
  2267.  
  2268.     // Adjust the "used" shape for the new frame shape.
  2269.     TempODShape usedShape = this->CalcNewUsedShape(ev, frame);
  2270.     frame->ChangeUsedShape(ev, usedShape, kODNULL);
  2271. }
  2272.  
  2273. //------------------------------------------------------------------------------
  2274. // Method:        Open
  2275. // Origin:        ODPart
  2276. //
  2277. // Description:    This method is called when OpenDoc, a containing part, or the
  2278. //                active editor would like to open a frame into a seperate window.
  2279. //                If a source frame is passed into this method, the editor is
  2280. //                being asked one of two things. If the frame is the root, we are
  2281. //                being asked to open an existing document. If the frame is not
  2282. //                the root, we are being asked to open a part window. If a source
  2283. //                frame is not specified, the editor is being asked to open a new
  2284. //                window.
  2285. //------------------------------------------------------------------------------
  2286.  
  2287. ODID SoundEditor::Open( Environment*    ev, 
  2288.                         ODFrame*        frame )
  2289. {
  2290.     SOM_Trace("SoundEditor","Open");
  2291.  
  2292.     ODID windowID;
  2293.     TempODWindow window = kODNULL;
  2294.     WindowProperties* windowProperties = kODNULL;
  2295.  
  2296.     ODVolatile(window);
  2297.     ODVolatile(windowProperties);
  2298.  
  2299.     TRY
  2300.         ODBoolean adjustWindow = kODFalse;
  2301.     
  2302.         // Because the frame parameter being passed to us can be one of
  2303.         // three things, we must determine what it is; either the root
  2304.         // frame of a existing document, the source frame for a part
  2305.         // window, or null if we are opening a new document.
  2306.     
  2307.         if ( frame == kODNULL )
  2308.         {
  2309.             // Calculate the bounding rectangle for a new window
  2310.             Rect windowRect = this->CalcPartWindowSize(ev, kODNULL);
  2311.             // Get the default setting for a document window.
  2312.             windowProperties = this->GetDefaultWindowProperties(ev, kODNULL, &windowRect);
  2313.             // Create a Mac Window and register it with OpenDoc.
  2314.             window = this->CreateWindow(ev, kODNULL, kODFrameObject, windowProperties);
  2315.         }
  2316.         else if ( frame->IsRoot(ev) )
  2317.         {
  2318.             windowProperties = GetSavedWindowProperties(ev, frame);
  2319.  
  2320.             if ( windowProperties == kODNULL )
  2321.             {
  2322.                 // Calculate the bounding rectangle for a new window
  2323.                 Rect windowRect = this->CalcPartWindowSize(ev, kODNULL);
  2324.                 // Get the default setting for a document window.
  2325.                 windowProperties = this->GetDefaultWindowProperties(ev, kODNULL, &windowRect);
  2326.             }
  2327.             
  2328.             adjustWindow = kODTrue;
  2329.  
  2330.             // Create a Mac Window and register it with OpenDoc.
  2331.             window = this->CreateWindow(ev, frame, kODFrameObject, windowProperties);
  2332.             
  2333.             // We release the source frame here because we didn't call
  2334.             // EndGetWindowProperties and becuase we are done with it.
  2335.             ODReleaseObject(ev, windowProperties->sourceFrame);
  2336.         }
  2337.         else // frame is a source frame
  2338.         {
  2339.             window = this->AcquireFramesWindow(ev, frame);
  2340.     
  2341.             if ( window == kODNULL )
  2342.             {
  2343.                 // Calculate the bounding rectangle for a new window
  2344.                 Rect windowRect = this->CalcPartWindowSize(ev, frame);
  2345.                 // Get the default setting for a document window.
  2346.                 windowProperties = this->GetDefaultWindowProperties(ev, frame, &windowRect);
  2347.                 // Create a Mac Window and register it with OpenDoc.
  2348.                 window = this->CreateWindow(ev, kODNULL, kODFrameObject, windowProperties);
  2349.                                 
  2350.                 // Tell the source frame that it is opened in a part window.
  2351.                 CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  2352.                 frameInfo->SetPartWindow(ev, window);
  2353.  
  2354.                 this->ZoomPartWindow(ev, frame, window, kWindowOpening);
  2355.             }
  2356.         }
  2357.         
  2358.         // Create the window's root facet.
  2359.         window->Open(ev);
  2360.         
  2361.         // Adjust the root frame to match the window in case it was sized
  2362.         // differently when the document was created (i.e. it was dragged
  2363.         // from a container where it was sized differently).
  2364.         if ( adjustWindow )
  2365.         {
  2366.             WASSERT(frame != kODNULL);
  2367.             
  2368.             TempODShape frameShape = frame->AcquireFrameShape(ev, kODNULL);
  2369.             ODRect bbox;
  2370.             frameShape->GetBoundingBox(ev, &bbox);
  2371.             Rect bounds;
  2372.             FixedToIntRect(bbox, bounds);
  2373.             
  2374.             if ( bounds.right - bounds.left != kControlsWidth ||
  2375.                  bounds.bottom - bounds.top != kControlsHeight )
  2376.             {
  2377.                 window->AdjustWindowShape(ev);
  2378.                 // ••• This causes the document to be dirtied!!!  Do I need a better way 
  2379.                 //     to do this?  We basically need a way to resize the frame if it's
  2380.                 //     shape is not correct.
  2381.             }
  2382.         }
  2383.         
  2384.         // Make the window visible.
  2385.         window->Show(ev);
  2386.         // Activate and select the window.
  2387.         window->Select(ev);
  2388.     
  2389.         // Cleanup allocate memory.
  2390.         ODDeleteObject(windowProperties);
  2391.         
  2392.         // Get window id to return.
  2393.         windowID = (window ? window->GetID(ev) : kODNULLID);
  2394.     
  2395.     CATCH_ALL
  2396.         // If we threw early, the source frame's refcount may be too high.
  2397.         if ( windowProperties )
  2398.             ODSafeReleaseObject(windowProperties->sourceFrame);
  2399.         // Cleanup the created items.
  2400.         ODDeleteObject(windowProperties);
  2401.         windowID = kODNULLID;
  2402.         // Alert the caller.
  2403.         RERAISE;
  2404.     ENDTRY
  2405.  
  2406.     return windowID;
  2407. }
  2408.  
  2409. //------------------------------------------------------------------------------
  2410. // Method:        AcquireFramesWindow
  2411. // Origin:        SoundEditor
  2412. //
  2413. // Description:    This method is called by the part when a frame, that has been
  2414. //                previously opened, is being opened again.
  2415. //
  2416. //                The method retrieves the existing window for the frame and
  2417. //                returns it.                
  2418. //------------------------------------------------------------------------------
  2419.  
  2420. ODWindow* SoundEditor::AcquireFramesWindow( Environment*    ev,
  2421.                                                ODFrame*        frame )
  2422. {
  2423.     SOM_Trace("SoundEditor","AcquireFramesWindow");
  2424.  
  2425.     CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  2426.     ODWindow* window = frameInfo->AcquirePartWindow(ev);
  2427.  
  2428.     return window;
  2429. }
  2430.     
  2431. //------------------------------------------------------------------------------
  2432. // Method:        CreateWindow
  2433. // Origin:        SoundEditor
  2434. //
  2435. // Description:    This method is called by the part when a window needs to be
  2436. //                created for a frame being opened.
  2437. //
  2438. //                The part uses the information passed in windowProperties to 
  2439. //                create the appropriate window. The generated window is
  2440. //                registered with OpenDoc as a new window (RegisterWindow) or as a
  2441. //                window from an existing document (RegisterWindowForFrame).
  2442. //------------------------------------------------------------------------------
  2443.  
  2444. ODWindow* SoundEditor::CreateWindow( Environment*         ev,
  2445.                                      ODFrame*            frame,
  2446.                                      ODType                frameType,
  2447.                                      WindowProperties*    windowProperties)
  2448. {
  2449.     SOM_Trace("SoundEditor","CreateWindow");
  2450.  
  2451.     ODPlatformWindow    platformWindow    = kODNULL;
  2452.     ODWindow*            window            = kODNULL;
  2453.     
  2454.     // Using the name and the calculated rectangle, create a new window.
  2455.     // Note that we are allocating the window record in temp mem using
  2456.     // the OpenDoc memory mgr. This helps reduce app heap usage.
  2457.     // In addition, OpenDoc requires that all new windows be initially hidden
  2458.     // so that it can correctly layer windows/palettes.
  2459.     platformWindow = NewCWindow((Ptr) ODNewPtr(sizeof(WindowRecord)),
  2460.                                 &(windowProperties->boundsRect),
  2461.                                 windowProperties->title,
  2462.                                 kODFalse, /* visible */
  2463.                                 windowProperties->procID,
  2464.                                 (WindowPtr) -1L,
  2465.                                 windowProperties->hasCloseBox,
  2466.                                 windowProperties->refCon);
  2467.  
  2468.     if ( platformWindow )
  2469.     {
  2470.         TRY
  2471.             ODWindowState* windowState = fSession->GetWindowState(ev);
  2472.             
  2473.             ODBoolean saveWindow = ODISOStrEqual(frameType, kODFrameObject);
  2474.             
  2475.             // Tell the window object that we will be disposing the window record
  2476.             // when the root frame is closed/removed.
  2477.             ODBoolean shouldDispose = kODFalse;
  2478.                                 
  2479.             // Determine whether we are creating a new window (frame is null),
  2480.             // or opening a previous saved window (frame is valid).
  2481.             
  2482.             if ( frame == kODNULL )
  2483.             {                                
  2484.                 // Tell OpenDoc about it by creating an OpenDoc window object.
  2485.                 window = windowState->
  2486.                             RegisterWindow(ev, 
  2487.                                 platformWindow,                    // Macintosh WindowPtr
  2488.                                 frameType,                        // Frame type (Persistent/Non-persistent)
  2489.                                 windowProperties->isRootWindow,    // Is this a document window?
  2490.                                 windowProperties->isResizable,    // Is this window resizeable?
  2491.                                 windowProperties->isFloating,    // Is this window floating?
  2492.                                 saveWindow,                        // Should this window be persistent?
  2493.                                 shouldDispose,                    // (see comment above)
  2494.                                 fSelf,                            // Part reference to us
  2495.                                 gGlobals->fFrameView,            // What view should the window have?
  2496.                                 gGlobals->fControlsPresentation,// What presentation should the window have?
  2497.                                 windowProperties->sourceFrame);    // The display frame being opened, if any
  2498.             }
  2499.             else
  2500.             {
  2501.                 // Tell OpenDoc about it by creating an OpenDoc window object.
  2502.                 window = windowState->
  2503.                             RegisterWindowForFrame(ev, 
  2504.                                 platformWindow,                    // Macintosh WindowPtr
  2505.                                 frame,                             // Frame type (Persistent/Non-persistent)
  2506.                                 windowProperties->isRootWindow,    // Is this a document window?
  2507.                                 windowProperties->isResizable,    // Is this window resizeable?
  2508.                                 windowProperties->isFloating,    // Is this window floating?
  2509.                                 saveWindow,                        // Should this window be persistent?
  2510.                                 shouldDispose,                    // (see comment above)
  2511.                                 windowProperties->sourceFrame);    // The display frame being opened, if any
  2512.             }
  2513.             
  2514.         CATCH_ALL
  2515.             // Cleanup Macintosh Window.
  2516.             CloseWindow(platformWindow);
  2517.             ODDisposePtr(platformWindow);
  2518.             // Get the right error message for the problem.
  2519.             ODSShort errMsgNum = (!frame && windowProperties->sourceFrame)
  2520.                                     ? kErrCantOpenPartWindow : kErrCantOpenDocWindow;
  2521.             
  2522.             // Alert the user of the problem.
  2523.             this->DoDialogBox(ev, frame, kErrorBoxID, errMsgNum);
  2524.             // Change the exception value, so the DocShell doesn't display an
  2525.             // error dialog.
  2526.             SetErrorCode(kODErrAlreadyNotified);
  2527.             // Alert the caller.
  2528.             RERAISE;
  2529.         ENDTRY
  2530.     }
  2531.  
  2532.     return window;
  2533. }
  2534.  
  2535. //------------------------------------------------------------------------------
  2536. // Method:        CleanupWindow
  2537. // Origin:        SoundEditor
  2538. //
  2539. // Description:    This method is called by the part when a window needs to be
  2540. //                cleaned up for a frame being closed/removed.
  2541. //
  2542. //                The part deallocates the window buffer allocated in the
  2543. //                CreateWindow() method.
  2544. //------------------------------------------------------------------------------
  2545.  
  2546. void SoundEditor::CleanupWindow( Environment*     ev,
  2547.                                  ODFrame*        frame )
  2548. {
  2549.     SOM_Trace("SamplePart","CleanupWindow");
  2550.     
  2551.        TRY
  2552.         CFrameInfo*    frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  2553.         if ( frameInfo->ShouldDisposeWindow() )
  2554.         {
  2555.             TempODWindow window = frame->AcquireWindow(ev);
  2556.             THROW_IF_NULL(window);
  2557.             
  2558.             ODPlatformWindow windowPtr = window->GetPlatformWindow(ev);
  2559.             CloseWindow(windowPtr);
  2560.             ODDisposePtr(windowPtr);
  2561.         }
  2562.     CATCH_ALL
  2563.         this->DoDialogBox(ev, frame, kErrorBoxID, kErrWindowGone);
  2564.         // consume excpetion because it's not fatal.
  2565.     ENDTRY
  2566. }
  2567.  
  2568. //------------------------------------------------------------------------------
  2569. // Method:        GetDefaultWindowProperties
  2570. // Origin:        SoundEditor
  2571. //
  2572. // Description:    This method is called by the part when a new window is being
  2573. //                created. The method examines the frame which is being opened
  2574. //                generates the default window parameters to pass to the
  2575. //                Mac Toolbox.
  2576. //------------------------------------------------------------------------------
  2577.  
  2578. WindowProperties*
  2579. SoundEditor::GetDefaultWindowProperties( Environment*     ev,
  2580.                                          ODFrame*        sourceFrame,
  2581.                                          Rect*            windowRect )
  2582. {
  2583.     SOM_Trace("SoundEditor","GetDefaultWindowProperties");
  2584.     
  2585.     WindowProperties* windowProperties = new WindowProperties;
  2586.  
  2587.     TRY
  2588.         // Calculate the offset for the window based on the sourceFrame.    
  2589.         if ( sourceFrame )
  2590.             this->CalcPartWindowPosition(ev, sourceFrame, windowRect);
  2591.         else
  2592.             OffsetRect(windowRect, kALittleNudge,
  2593.                         GetMBarHeight() + kMacWindowTitleBarHeight);        
  2594.             
  2595.         windowProperties->boundsRect = *windowRect;
  2596.     
  2597.         // Get the part's name to use for the new window.
  2598.         TempODIText windowName = GetPartName(ev, fSelf, kODCategorySampledSound);
  2599.         // Convert the ODIText into a Pascal string.
  2600.         GetITextString(windowName, windowProperties->title);
  2601.         
  2602.         // Fill in the other fields of the Window Properties struct.
  2603.         windowProperties->procID = noGrowDocProc;
  2604.         windowProperties->hasCloseBox = kODTrue;
  2605.         windowProperties->refCon = (long) kODNULL;
  2606.         windowProperties->wasVisible = kODFalse;
  2607.         windowProperties->isResizable = kODFalse;
  2608.         windowProperties->isFloating = kODFalse;
  2609.         windowProperties->isRootWindow = sourceFrame ? kODFalse : kODTrue;
  2610.         windowProperties->shouldShowLinks = kODFalse;
  2611.         windowProperties->sourceFrame = sourceFrame;
  2612.     CATCH_ALL
  2613.         // Clean up and...
  2614.         ODDeleteObject(windowProperties);
  2615.         // Alert the caller.
  2616.         RERAISE;
  2617.     ENDTRY
  2618.     
  2619.     return windowProperties;
  2620. }
  2621.  
  2622. //------------------------------------------------------------------------------
  2623. // Method:        GetSavedWindowProperties
  2624. // Origin:        SoundEditor
  2625. //
  2626. // Description:    This method is called by the part to read in saved information
  2627. //                for a window from an existing document.
  2628. //------------------------------------------------------------------------------
  2629.  
  2630. WindowProperties*
  2631. SoundEditor::GetSavedWindowProperties( Environment*     ev,
  2632.                                        ODFrame*            frame )
  2633. {
  2634.     SOM_Trace("SoundEditor","GetSavedWindowProperties");
  2635.  
  2636.     WindowProperties* windowProperties = new WindowProperties;
  2637.     
  2638.     // If we fail to load the window properties from storage, delete
  2639.     // the structure so the calling code will behave appropriately.    
  2640.     if ( BeginGetWindowProperties(ev, frame, windowProperties) )
  2641.     {
  2642.         TRY
  2643.             // Note: We don't call EndGetWindowProperties because it releases the source
  2644.             // frame, which we will need after this method returns.
  2645.     
  2646.             // Get the part's name to use for the new window.
  2647.             TempODIText windowName = GetPartName(ev, fSelf, kODCategorySampledSound);
  2648.             // Convert the ODIText into a Pascal string.
  2649.             GetITextString(windowName, windowProperties->title);
  2650.         
  2651.             // Verify the window is still visible on a monitor.
  2652.     
  2653.             RgnHandle windowRgn = ODNewRgn();
  2654.             ODBoolean repositionWindow = kODFalse;
  2655.             
  2656.             // We are only concerned with the window's title bar being
  2657.             // visible, so calcuate the titlebar rect from the current
  2658.             // window bounds.
  2659.             Rect adjustedBounds = windowProperties->boundsRect;
  2660.             adjustedBounds.bottom = adjustedBounds.top;
  2661.             adjustedBounds.top -= kMacWindowTitleBarHeight;
  2662.             
  2663.             // Intersect the monitor's region
  2664.             RectRgn(windowRgn, &adjustedBounds);
  2665.             SectRgn(windowRgn, GetGrayRgn(), windowRgn);
  2666.             
  2667.             if ( !EmptyRgn(windowRgn) )
  2668.             {
  2669.                 // If the visible portion of the window is too small, we need
  2670.                 // to reposition it.
  2671.                 Rect intersectedBounds = (**windowRgn).rgnBBox;
  2672.                 if ( (intersectedBounds.right - intersectedBounds.left < kMinHorzVisPortion) ||
  2673.                      (intersectedBounds.bottom - intersectedBounds.top < kMinVertVisPortion) )
  2674.                     repositionWindow = kODTrue;
  2675.             }
  2676.             else
  2677.             {
  2678.                 // If the window is completely offscreen, we need to reposition it.
  2679.                 repositionWindow = kODTrue;
  2680.             }
  2681.             ODDisposeHandle((ODHandle) windowRgn);
  2682.             
  2683.             // If not, we need to move it so the user can see it.
  2684.             if ( repositionWindow )
  2685.             {
  2686.                 Rect windowRect = (windowProperties->boundsRect);
  2687.                 // Move the window to {0,0} coordinates.
  2688.                 OffsetRect(&windowRect, -windowRect.left, -windowRect.top);
  2689.                 // Now move the window to the default window position.
  2690.                 OffsetRect(&windowRect, kALittleNudge, GetMBarHeight() + kMacWindowTitleBarHeight);
  2691.                 // Save the new window position in our windowProperties.
  2692.                 windowProperties->boundsRect = windowRect;
  2693.             }
  2694.         CATCH_ALL
  2695.             // Clean up and...
  2696.             ODDeleteObject(windowProperties);
  2697.             // Consume the exception.  
  2698.             // Caller should try to use the default window properties.
  2699.         ENDTRY
  2700.     }
  2701.     else
  2702.     {
  2703.         // If we were unable to re-load window properties, dispose of the
  2704.         // struct.
  2705.         ODDeleteObject(windowProperties);
  2706.     }
  2707.             
  2708.     return windowProperties;
  2709. }
  2710.  
  2711. //------------------------------------------------------------------------------
  2712. // Method:        CalcPartWindowSize
  2713. // Origin:        SoundEditor
  2714. //
  2715. // Description:    This method is called by the part to determine what size a new
  2716. //                window shoud be.
  2717. //------------------------------------------------------------------------------
  2718.  
  2719. Rect SoundEditor::CalcPartWindowSize( Environment*    ev,
  2720.                                       ODFrame*        sourceFrame )
  2721. {
  2722.     SOM_Trace("SoundEditor","CalcPartWindowSize");
  2723.  
  2724.     Rect windowRect;
  2725.  
  2726.     // Our frame view has a static size.
  2727.     SetRect(&windowRect, 0, 0, kControlsWidth, kControlsHeight);
  2728.  
  2729.     return windowRect;
  2730. }
  2731.  
  2732. //------------------------------------------------------------------------------
  2733. // Method:        CalcPartWindowPosition
  2734. // Origin:        SoundEditor
  2735. //
  2736. // Description:    This method is called by the part to determine where to align
  2737. //                the new window (top left corner of the screen or tiled to a
  2738. //                frame).
  2739. //------------------------------------------------------------------------------
  2740.  
  2741. Rect SoundEditor::CalcPartWindowPosition( Environment*    ev,
  2742.                                           ODFrame*        frame,
  2743.                                           Rect*            partWindowBounds )
  2744. {
  2745.     SOM_Trace("SoundEditor","CalcPartWindowPosition");
  2746.  
  2747.     ODFacet*    activeFacet;
  2748.     ODRect        bbox;
  2749.     Rect        bounds;
  2750.     
  2751.     // We need to know which facet of the frame we are opening to position
  2752.     // the part window.
  2753.     activeFacet = this->GetActiveFacetForFrame(ev, frame);
  2754.     
  2755.     // This should never occur, but if it did, it would be fatal.
  2756.     // So we will just pass back the same rectangle.
  2757.     if ( activeFacet == kODNULL )
  2758.         return *partWindowBounds;
  2759.     
  2760.     // For the purposes of tiling, we need the to know the area of the
  2761.     // document the frame occupies. We do this by getting the bouding
  2762.     // box and offsetting it by the aggregate external window transform
  2763.     // of the facet.
  2764.     
  2765.     TempODShape frameShape = activeFacet->GetFrame(ev)->AcquireFrameShape(ev, kODNULL);
  2766.     TempODTransform windowFrameTransform = activeFacet->AcquireWindowFrameTransform(ev, kODNULL);
  2767.     TempODShape boundsShape = frameShape->Copy(ev);
  2768.  
  2769.     // Translate the bounds rect into window coordinates.
  2770.     boundsShape->Transform(ev, windowFrameTransform);
  2771.     
  2772.     // Get and convert the bounding box into a QuickDraw rectangle.
  2773.     boundsShape->GetBoundingBox(ev, &bbox);
  2774.     FixedToIntRect(bbox, bounds);
  2775.         
  2776.     // We then call our method to tile the part window.
  2777.     *partWindowBounds = TilePartWindow(ev, &bounds, partWindowBounds);
  2778.     
  2779.     // Set the port and origin so we can convert the rect to
  2780.     // global Window Mgr coordinates.
  2781.     SetPort(activeFacet->GetCanvas(ev)->GetQDPort(ev));
  2782.     SetOrigin(0,-kMacWindowTitleBarHeight);
  2783.     
  2784.     // Convert the local coordinates to global Window Mgr coordinates.
  2785.     LocalToGlobal((Point*) &partWindowBounds->top);
  2786.     LocalToGlobal((Point*) &partWindowBounds->bottom);
  2787.     
  2788.     return *partWindowBounds;
  2789. }
  2790.  
  2791. //------------------------------------------------------------------------------
  2792. // Method:        ZoomPartWindow
  2793. // Origin:        SoundEditor
  2794. //
  2795. // Description:    This method is called by the part when a frame is being opened
  2796. //                or closed in the case that zooming rectangles should be shown.                
  2797. //------------------------------------------------------------------------------
  2798.  
  2799. void SoundEditor::ZoomPartWindow( Environment*        ev,
  2800.                                      ODFrame*            frame,
  2801.                                      ODWindow*            window,
  2802.                                      ODBoolean            openingWindow )
  2803. {
  2804.     SOM_Trace("SamplePart","ZoomPartWindow");
  2805.  
  2806.     const ODSShort kNumZoomSteps = 12;
  2807.  
  2808.     Rect frameRect;
  2809.     {
  2810.         // We need to know which facet of the frame we are opening to position
  2811.         // the child window.
  2812.         ODFacet* zoomFacet = kODNULL;
  2813.         
  2814.         TRY
  2815.             zoomFacet = this->GetActiveFacetForFrame(ev, frame);
  2816.         CATCH_ALL
  2817.         ENDTRY
  2818.         
  2819.         // In case there isn't an active facet for the frame, .
  2820.         if ( zoomFacet == kODNULL )
  2821.         {
  2822.             TempODFrameFacetIterator iter(ev, frame);
  2823.             zoomFacet = iter.First();
  2824.         }
  2825.         
  2826.         if ( zoomFacet != kODNULL )
  2827.         {
  2828.             // For doing the zoom rects, we need the to know the area of the
  2829.             // document the frame occupies. We do this by getting the bouding
  2830.             // box and offsetting it by the aggregate external window transform
  2831.             // of the facet.
  2832.             TempODTransform windowFrameTransform =
  2833.                                 zoomFacet->AcquireWindowFrameTransform(ev, kODNULL);
  2834.             TempODShape boundsShape =
  2835.                                 ODCopyAndRelease(ev, zoomFacet->GetFrame(ev)
  2836.                                                     ->AcquireFrameShape(ev, kODNULL));
  2837.             
  2838.             // Translate the bounds rect into window coordinates.
  2839.             boundsShape->Transform(ev, windowFrameTransform);
  2840.             
  2841.             // Get and convert the bounding box into a QuickDraw rectangle.
  2842.             ODRect bbox;
  2843.             boundsShape->GetBoundingBox(ev, &bbox);
  2844.             FixedToIntRect(bbox, frameRect);
  2845.                 
  2846.             // Set the port and origin so we can convert the rect to
  2847.             // global Window Mgr coordinates.
  2848.             SetPort(zoomFacet->GetCanvas(ev)->GetQDPort(ev));
  2849.             SetOrigin(0, 0);
  2850.             
  2851.             // Convert the local coordinates to global Window Mgr coordinates.
  2852.             LocalToGlobal((Point*) &frameRect.top);
  2853.             LocalToGlobal((Point*) &frameRect.bottom);
  2854.         }
  2855.     }
  2856.     
  2857.     Rect windowRect;
  2858.     {
  2859.         ODPlatformWindow platformWindow = window->GetPlatformWindow(ev);
  2860.  
  2861.         windowRect = platformWindow->portRect;
  2862.         windowRect.top -= kMacWindowTitleBarHeight;
  2863.         
  2864.         // Set the port and origin so we can convert the rect to
  2865.         // global Window Mgr coordinates.
  2866.         SetPort(platformWindow);
  2867.         SetOrigin(0, 0);
  2868.         
  2869.         // Convert the local coordinates to global Window Mgr coordinates.
  2870.         LocalToGlobal((Point*) &windowRect.top);
  2871.         LocalToGlobal((Point*) &windowRect.bottom);
  2872.     }
  2873.     
  2874.     Rect fromRect = openingWindow ? frameRect  : windowRect;
  2875.     Rect toRect   = openingWindow ? windowRect : frameRect;
  2876.     
  2877.     if ( !openingWindow )
  2878.         window->Hide(ev);
  2879.  
  2880.     ZoomRects(&fromRect, &toRect, kNumZoomSteps, 
  2881.               openingWindow ? zoomAccelerate : zoomDecelerate);
  2882. }
  2883.  
  2884. //------------------------------------------------------------------------------
  2885. // Method:        GetActiveFacetForFrame
  2886. // Origin:        SoundEditor
  2887. //
  2888. // Description:    This method is called by the part when it needs to know what the
  2889. //                current active facet is.
  2890. //
  2891. //                The part uses this method specifically to find the facet of a
  2892. //                source frame when opening a part window.
  2893. //------------------------------------------------------------------------------
  2894.  
  2895. ODFacet* SoundEditor::GetActiveFacetForFrame( Environment*    ev,
  2896.                                               ODFrame*        frame )
  2897. {
  2898.     SOM_Trace("SoundEditor","GetActiveFacetForFrame");
  2899.  
  2900.     ODFacet*        facet = kODNULL;
  2901.     CFrameInfo*        frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  2902.     
  2903.     // If the frame is active, and it should be, get the active facet
  2904.     // from the frame state info.
  2905.     if ( frameInfo->IsFrameActive() )
  2906.     {
  2907.         facet = frameInfo->GetActiveFacet();
  2908.     }
  2909.     else
  2910.     // Otherwise, iterate over the display frames looking for one
  2911.     // that has an active facet... there should be at least one.
  2912.     {
  2913.         for ( TempODFrameFacetIterator selectedFacet(ev, frame); 
  2914.               selectedFacet.IsNotComplete(); selectedFacet.Next() )
  2915.         {
  2916.             if ( selectedFacet->IsSelected(ev) )
  2917.             {
  2918.                 facet = selectedFacet;
  2919.                 break;
  2920.             }
  2921.         }
  2922.     }
  2923.     
  2924.     // If there are no active facets anywhere, this method should never
  2925.     // have been called, so signal an error.
  2926.     if ( facet == kODNULL )
  2927.         THROW(kODErrInvalidFrame);
  2928.         
  2929.     return facet;
  2930. }
  2931.  
  2932. //------------------------------------------------------------------------------
  2933. // Method:        CalcNewUsedShape
  2934. // Origin:        SoundEditor
  2935. //
  2936. // Description:    This method is called in response to one of display frame's view
  2937. //                being changed. The method calculates the appropriate usedShape
  2938. //                for the new view type.
  2939. //------------------------------------------------------------------------------
  2940.  
  2941. ODShape* SoundEditor::CalcNewUsedShape( Environment*    ev,
  2942.                                           ODFrame*        frame )
  2943. {
  2944.     SOM_Trace("SoundEditor","CalcNewUsedShape");
  2945.  
  2946.     ODShape* usedShape = kODNULL;        ODVolatile(usedShape);
  2947.     RgnHandle usedRgn;                    ODVolatile(usedRgn);
  2948.  
  2949.     // If the view is "frame", we intentionally return a nil shape;
  2950.     // doing so, will reset the used shape to equal the frame shape.
  2951.  
  2952.     ODTypeToken view = frame->GetViewType(ev);
  2953.  
  2954.     if ( view == gGlobals->fLargeIconView ||
  2955.          view == gGlobals->fSmallIconView ||
  2956.          view == gGlobals->fThumbnailView )
  2957.     {
  2958.         TRY
  2959.             Rect bounds;
  2960.             usedRgn = ODNewRgn();
  2961.  
  2962.             if ( view == gGlobals->fLargeIconView || view == gGlobals->fSmallIconView )
  2963.             {
  2964.                 CUsingLibraryResources res;
  2965.  
  2966.                 SetRect(&bounds, 0, 0,
  2967.                         (view == gGlobals->fLargeIconView) ? kODLargeIconSize : kODSmallIconSize,
  2968.                         (view == gGlobals->fLargeIconView) ? kODLargeIconSize : kODSmallIconSize);
  2969.  
  2970.                 THROW_IF_ERROR(IconIDToRgn(usedRgn, &bounds, atAbsoluteCenter, kBaseResourceID));
  2971.             }
  2972.             else if ( view == gGlobals->fThumbnailView )
  2973.             {
  2974.                 PicHandle thumbnail = this->GenerateThumbnail(ev, frame);
  2975.  
  2976.                 bounds = (**thumbnail).picFrame;
  2977.                 RectRgn(usedRgn, &bounds);
  2978.             }
  2979.             
  2980.             usedShape = frame->CreateShape(ev);
  2981.             usedShape->SetQDRegion(ev, usedRgn);
  2982.                             
  2983.         CATCH_ALL
  2984.             ODSafeReleaseObject(usedShape);
  2985.             ODDisposeHandle((ODHandle)usedRgn);
  2986.             usedShape = kODNULL;
  2987.         ENDTRY
  2988.     }
  2989.         
  2990.     return usedShape;
  2991. }
  2992.  
  2993. //------------------------------------------------------------------------------
  2994. // Method:        UpdateFrame
  2995. // Origin:        SoundEditor
  2996. //
  2997. // Description:    This method is called in response to one of our
  2998. //------------------------------------------------------------------------------
  2999.  
  3000. void
  3001. SoundEditor::UpdateFrame( Environment*    ev,
  3002.                           ODFrame*        frame,
  3003.                           ODTypeToken    view,
  3004.                           ODShape*        usedShape )
  3005. {
  3006.     SOM_Trace("SoundEditor","UpdateFrame");
  3007.  
  3008.     TRY
  3009.         // Update the frame to have the new view and UsedShape.
  3010.         frame->Invalidate(ev, kODNULL, kODNULL);
  3011.         frame->SetViewType(ev, view);
  3012.         frame->ChangeUsedShape(ev, usedShape, kODNULL);
  3013.         frame->Invalidate(ev, kODNULL, kODNULL);
  3014.     CATCH_ALL
  3015.         // Failing isn't great, but we can live with it, so don't set ev.
  3016.     ENDTRY
  3017. }
  3018.  
  3019. //==============================================================================
  3020. #pragma mark    • Imaging •
  3021. //==============================================================================
  3022.  
  3023. //------------------------------------------------------------------------------
  3024. // Method:        FacetAdded
  3025. // Origin:        ODPart
  3026. //
  3027. // Description:    This method is called when any part adds a facet to
  3028. //                one of our display frames.
  3029. //
  3030. //                The part calls ViewTypeChanged to load the appropriate
  3031. //                resource for display in this facet and then activates
  3032. //                the frame if we are the root part of an active window.
  3033. //------------------------------------------------------------------------------
  3034.  
  3035. void SoundEditor::FacetAdded( Environment*    ev,
  3036.                               ODFacet*        facet )
  3037. {
  3038.     SOM_Trace("SoundEditor","FacetAdded");
  3039.  
  3040.     ODFrame* frame = facet->GetFrame(ev);
  3041.     CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  3042.  
  3043.     // If a root facet is being added, the frame should be activated when the
  3044.     // window is "selected". This prevents OpenDoc from getting into an
  3045.     // ambiguous state of no part having the selection focus.
  3046.  
  3047.     if ( frame->IsRoot(ev) )
  3048.     {
  3049.         frameInfo->SetActiveFacet(facet);
  3050.         frameInfo->SetFrameReactivate(kODTrue);
  3051.     }
  3052.     
  3053.     // If a frame had all of its facets removed, the frame would have
  3054.     // hidden any of its part windows. If the frame becomes visible again,
  3055.     // by having a facet added to it, we will "show" the part window for
  3056.     // the frame.
  3057.     
  3058.     if ( CountFacets(ev, frame) == 1 )
  3059.     {
  3060.         TempODWindow window = frameInfo->AcquirePartWindow(ev);
  3061.         if ( window )
  3062.             window->Show(ev);
  3063.     }
  3064. }
  3065.  
  3066. //------------------------------------------------------------------------------
  3067. // Method:        FacetRemoved
  3068. // Origin:        ODPart
  3069. //
  3070. // Description:    This method is called when any part removes a facet
  3071. //                from one of our display frames.
  3072. //
  3073. //                The part just removes the "active" note from the
  3074. //                appropriate display frame if necessary since this
  3075. //                facet will not be available, nor active, again.
  3076. //------------------------------------------------------------------------------
  3077.  
  3078. void SoundEditor::FacetRemoved( Environment*    ev,
  3079.                                 ODFacet*        facet )
  3080. {
  3081.     SOM_Trace("SoundEditor","FacetRemoved");
  3082.  
  3083.     ODFrame*    frame = facet->GetFrame(ev);
  3084.     TempODFrame    containingFrame = frame->AcquireContainingFrame(ev);
  3085.     CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  3086.  
  3087.     // If the facet was the active facet, it can no longer be.
  3088.        if ( ODObjectsAreEqual(ev, frameInfo->GetActiveFacet(), facet) )
  3089.         frameInfo->SetActiveFacet(kODNULL);
  3090.  
  3091.     // If a frame has all of its facets removed, the frame becomes
  3092.     // "hidden". If the frame owns a part window, the part window should
  3093.     // also be hidden.
  3094.     
  3095.     if ( CountFacets(ev, frame) == 0 && containingFrame == kODNULL )
  3096.     {
  3097.         TempODWindow window = frameInfo->AcquirePartWindow(ev);
  3098.         if ( window ) 
  3099.             window->Hide(ev);
  3100.     }
  3101. }
  3102.  
  3103. //------------------------------------------------------------------------------
  3104. // Method:        Draw
  3105. // Origin:        ODPart
  3106. //
  3107. // Description:    This method is called when a facet of a part's display
  3108. //                frame intersects the invalidated portion of an OpenDoc
  3109. //                window. The invalidShape parameter passed in is the
  3110. //                portion of the facet which has been invalidated.
  3111. //
  3112. //                The part sets up the drawing environment using a
  3113. //                C++ helper class (CFocusDrawingEnv) and then calls the
  3114. //                appropriate drawing method based on the frame's
  3115. //                viewType.
  3116. //------------------------------------------------------------------------------
  3117.  
  3118. void SoundEditor::Draw( Environment*        ev,
  3119.                         ODFacet*            facet,
  3120.                         ODShape*            invalidShape )
  3121. {
  3122.     SOM_Trace("SoundEditor","Draw");
  3123.  
  3124.     // Focus the port and origin for drawing in our facet.
  3125.     // Note that this instance of the CFocus class
  3126.     // is being allocated on the stack. When the execution
  3127.     // leaves the scope of this method, the destructor (which
  3128.     // cleans up the drawing environment) is automatically
  3129.     // called.
  3130.     CFocus initiateDrawing(ev, facet);
  3131.     
  3132.     ODTypeToken view = facet->GetFrame(ev)->GetViewType(ev);
  3133.     
  3134.     if ( view == gGlobals->fLargeIconView || view == gGlobals->fSmallIconView )
  3135.         this->DrawIconView(ev, facet);
  3136.     else if ( view == gGlobals->fThumbnailView )
  3137.         this->DrawThumbnailView(ev, facet);
  3138.     else
  3139.         this->DrawFrameView(ev, facet);
  3140. }
  3141.  
  3142. //------------------------------------------------------------------------------
  3143. // Method:        DrawFrameView
  3144. // Origin:        SoundEditor
  3145. //
  3146. // Description:    This method is called by the part when the frame being
  3147. //                drawn is in "frame" view.
  3148. //
  3149. //------------------------------------------------------------------------------
  3150.  
  3151. void SoundEditor::DrawFrameView( Environment*    ev,
  3152.                                  ODFacet*        facet )
  3153. {
  3154.     SOM_Trace("SoundEditor","DrawFrameView");
  3155.  
  3156.     ODFrame* frame = facet->GetFrame(ev);
  3157.  
  3158.     GrafPtr thePort;
  3159.     GetPort(&thePort);
  3160.     EraseRect(&thePort->portRect);
  3161.  
  3162.     if ( frame->GetPresentation(ev) == gGlobals->fControlsPresentation )
  3163.     {
  3164.         Rect controlsRect;
  3165.         PicHandle controlsPict = this->LoadControls(ev, frame);
  3166.         SetRect(&controlsRect, 0, 0, kControlsWidth, kControlsHeight);
  3167.         DrawPicture(controlsPict, &controlsRect);
  3168.  
  3169.         // Draw the buttons.
  3170.         
  3171.         // Play button.
  3172.         if ( this->SoundPlaying() )
  3173.             this->DrawButton(ev, kPlayButton, kButtonStateOn);
  3174.         else
  3175.             this->DrawButton(ev, kPlayButton, this->IsPlayAvailable() 
  3176.                                                     ? kButtonStateNormal : kButtonStateDim);
  3177.         
  3178.         // Pause button.
  3179.         if ( this->SoundPaused() )
  3180.             this->DrawButton(ev, kPauseButton, kButtonStateOn);
  3181.         else
  3182.             this->DrawButton(ev, kPauseButton, this->IsPauseAvailable() 
  3183.                                                     ? kButtonStateNormal : kButtonStateDim);
  3184.         
  3185.         // Stop button.
  3186.         this->DrawButton(ev, kStopButton, this->IsStopAvailable() 
  3187.                                                 ? kButtonStateNormal : kButtonStateDim);
  3188.         
  3189.         // Record button.
  3190.         if ( this->SoundRecording() )
  3191.             this->DrawButton(ev, kRecordButton, kButtonStateOn);
  3192.         else
  3193.             this->DrawButton(ev, kRecordButton, this->IsRecordAvailable()
  3194.                                                     ? kButtonStateNormal : kButtonStateDim);
  3195.  
  3196.         // Draw the empty speaker icon to start with.
  3197.         DrawEditorPicture(kSpeaker0PICT, gGlobals->fSpeakerIconRect);
  3198.         
  3199.         if ( this->SoundPlaying() && !fPlayer->IsPaused() )
  3200.             DrawEditorPicture(kSpeaker0PICT + fPlayer->GetLevel(), gGlobals->fSpeakerIconRect);
  3201.  
  3202.         if ( this->SoundRecording() )
  3203.             DrawEditorPicture(kSpeaker0PICT + fRecorder->GetLevel(), gGlobals->fSpeakerIconRect);
  3204.  
  3205.         // Draw the progress bar
  3206.         
  3207.         // First find out the current seconds value
  3208.         // If playing, then number of seconds played
  3209.         // If recording, then number of seconds recorded
  3210.         // If valid sound already recorded, then its length
  3211.         // otherwise, just zero
  3212.         if ( this->SoundPlaying() )
  3213.             fCurrentTime = fPlayer->GetTime();
  3214.         else if ( this->SoundRecording() )
  3215.             fCurrentTime = fRecorder->GetTime();                    
  3216.         else if ( fSecsRecorded > 0 )
  3217.             fCurrentTime = fSecsRecorded;
  3218.         else
  3219.             fCurrentTime = 0;
  3220.         
  3221.         // Draw a background of off-record color to indicate old sound if not recording
  3222.         if ( !this->SoundRecording() )
  3223.         {
  3224.             ODSShort progBar, colorBar;
  3225.  
  3226.             if ( fMaxSeconds )
  3227.                 progBar = (fSecsRecorded * 100) / fMaxSeconds;
  3228.             else
  3229.                 progBar = 0;
  3230.             
  3231.             if ( fUnknownSound )
  3232.             {
  3233.                 progBar = 100;
  3234.                 colorBar = kInvalidPAT;            
  3235.             }
  3236.             else
  3237.             {
  3238.                 colorBar = kRecordOffPAT;
  3239.             }
  3240.             this->DrawProgressBar(ev, facet, progBar, colorBar);
  3241.         }
  3242.         
  3243.         // If we are recording or playing, need to draw the foreground bar
  3244.         if ( this->SoundRecording() || this->SoundPlaying() )
  3245.         {
  3246.             ODSShort progBar, colorBar;
  3247.  
  3248.             colorBar = this->SoundRecording() ? kRecordOnPAT : kPlayingPAT;
  3249.             
  3250.             if ( fMaxSeconds )
  3251.                 progBar = (fCurrentTime * 100) / fMaxSeconds;
  3252.             else
  3253.                 progBar = 0;
  3254.             
  3255.             if ( !fUnknownSound )
  3256.                 this->DrawProgressBar(ev, facet, progBar, colorBar);
  3257.         }
  3258.         
  3259.         // Draw the current and maximum recording times
  3260.         this->DrawCurrentTime(ev, facet, -1, fCurrentTime);
  3261.         DrawTime(ev, facet, gGlobals->fMaxTimeRect, 10, fMaxSeconds);
  3262.         
  3263.         if ( !frame->IsRoot(ev) )
  3264.             FrameRect(&controlsRect);
  3265.     }
  3266. }
  3267.  
  3268. //------------------------------------------------------------------------------
  3269. // Method:        DrawIconView
  3270. // Origin:        SoundEditor
  3271. //
  3272. // Description:    This method is called by the part when the frame being
  3273. //                drawn is in "standard icon" view.
  3274. //
  3275. //                The part uses the Icon Utilities toolbox manager to
  3276. //                aid in drawing icons in active windows. The Guidelines
  3277. //                require a different appearance for selected icons in
  3278. //                inactive windows, which we do manually.
  3279. //------------------------------------------------------------------------------
  3280.  
  3281. void SoundEditor::DrawIconView( Environment*    ev,
  3282.                                 ODFacet*        facet )
  3283. {
  3284.     SOM_Trace("SoundEditor","DrawIconView");
  3285.  
  3286.     IconTransformType     transformType    = ttNone;
  3287.     
  3288.     ODFrame*    frame        = facet->GetFrame(ev);
  3289.     ODTypeToken    viewType    = frame->GetViewType(ev);
  3290.     CFrameInfo*    frameInfo    = (CFrameInfo*) frame->GetPartInfo(ev);
  3291.     
  3292.     // Check to see if the facet is selected
  3293.     if ( facet->GetHighlight(ev) == kODFullHighlight )
  3294.         transformType = ttSelected;
  3295.     
  3296.     // Check to see if the frame has been opened into a part window.
  3297.     TempODWindow window = frameInfo->AcquirePartWindow(ev);
  3298.     if ( window && window->IsShown(ev) )
  3299.         transformType |= ttOpen;
  3300.     
  3301.     // Draw the icon.
  3302.     Rect iconRect;
  3303.     SetRect(&iconRect, 0, 0,
  3304.             (viewType == gGlobals->fLargeIconView) ? kODLargeIconSize : kODSmallIconSize,
  3305.             (viewType == gGlobals->fLargeIconView) ? kODLargeIconSize : kODSmallIconSize);
  3306.  
  3307.     CUsingLibraryResources res;
  3308.     PlotIconID(&iconRect, atAbsoluteCenter, transformType, kBaseResourceID);
  3309. }
  3310.  
  3311. //------------------------------------------------------------------------------
  3312. // Method:        DrawThumbnailView
  3313. // Origin:        SoundEditor
  3314. //
  3315. // Description:    This method is called by the part when the frame being
  3316. //                drawn is in "thumbnail" view.
  3317. //
  3318. //                The part uses a picture for its thumbnail view because
  3319. //                it has no intrinsic content. A picture resource is
  3320. //                probably not sufficient for parts with real content.
  3321. //------------------------------------------------------------------------------
  3322.  
  3323. void SoundEditor::DrawThumbnailView( Environment*    ev,
  3324.                                      ODFacet*        facet )
  3325. {
  3326.     SOM_Trace("SoundEditor","DrawThumbnailView");
  3327.  
  3328.     // Create or retrieve a cached thumbnail picture.
  3329.     PicHandle thumbnail = this->GenerateThumbnail(ev, facet->GetFrame(ev));
  3330.     
  3331.     Rect bounds = (**thumbnail).picFrame;
  3332.     DrawPicture(thumbnail, &bounds);
  3333. }
  3334.  
  3335. //------------------------------------------------------------------------------
  3336. // Method:        GeometryChanged
  3337. // Origin:        ODPart
  3338. //
  3339. // Description:    This method is called when the ExternalTransform or
  3340. //                ClipShape of a facet on one this part's display frames
  3341. //                changes.
  3342. //------------------------------------------------------------------------------
  3343.  
  3344. void SoundEditor::GeometryChanged( Environment*        ev,
  3345.                                    ODFacet*            facet,
  3346.                                    ODBoolean        clipShapeChanged,
  3347.                                    ODBoolean        externalTransformChanged )
  3348. {
  3349.     SOM_Trace("SoundEditor","GeometryChanged");
  3350.  
  3351.     if ( clipShapeChanged )
  3352.         // Specifying kODNULL means to invalidate the clipShape (which was
  3353.         // calculated from the usedShape).
  3354.         facet->Invalidate(ev, kODNULL, kODNULL);
  3355. }
  3356.  
  3357. //------------------------------------------------------------------------------
  3358. // Method:        HighlightChanged
  3359. // Origin:        ODPart
  3360. //
  3361. // Description:    This method is called when a facet's hilighting state has been
  3362. //                changed (i.e. an icon view is now open and so should show the
  3363. //                icon in the open state).
  3364. //------------------------------------------------------------------------------
  3365.  
  3366. void SoundEditor::HighlightChanged(Environment* ev, ODFacet* facet)
  3367. {
  3368.     ODFrame* frame = facet->GetFrame(ev);
  3369.     
  3370.     // The frame view has no "special" drawing characteristics
  3371.     // when opened or selected, so we don't need to update our
  3372.     // content.
  3373.     
  3374.     if ( frame->GetViewType(ev) != gGlobals->fFrameView )
  3375.         frame->Invalidate(ev, kODNULL, kODNULL);
  3376. }
  3377.  
  3378. //==============================================================================
  3379. #pragma mark    • Activation •
  3380. //==============================================================================
  3381.  
  3382. //------------------------------------------------------------------------------
  3383. // Method:        BeginRelinquishFocus
  3384. // Origin:        ODPart
  3385. //
  3386. // Description:    This method is called when another part (or possibly
  3387. //                ourself) is requesting a focus for one of its display
  3388. //                frames. Returning true means we are willing to give
  3389. //                up the requested focus.
  3390. //
  3391. //                The part willingly gives up any focus unless: 
  3392. //                it is the modal focus which we don't want to give up 
  3393. //                until we are completely done displaying a modal dialog or
  3394. //                it is the sound in/out focus and we are recording/playing.
  3395. //------------------------------------------------------------------------------
  3396.  
  3397. ODBoolean SoundEditor::BeginRelinquishFocus( Environment*    ev,
  3398.                                              ODTypeToken    focus,
  3399.                                              ODFrame*        ownerFrame,
  3400.                                              ODFrame*        proposedFrame )
  3401. {
  3402.     SOM_Trace("SoundEditor","BeginRelinquishFocus");
  3403.  
  3404.     ODBoolean willRelinquish = kODTrue;
  3405.  
  3406.     // Another part is trying to put up a Modal dialog while we
  3407.     // are currently displaying ours. Deny the request.
  3408.     if ( focus == gGlobals->fModalFocus )
  3409.     {
  3410.         TempODPart proposedPart = ODAcquirePart(ev,proposedFrame);
  3411.         if ( ODObjectsAreEqual(ev, proposedPart, fSelf) == kODFalse )
  3412.             willRelinquish = kODFalse;
  3413.     }
  3414.                 
  3415.     // Another part is trying to play a sound while we 
  3416.     // are currently playing ours.  Deny the request.
  3417.     if ( focus == gGlobals->fSoundOutFocus && this->SoundPlaying() )
  3418.         willRelinquish = kODFalse;
  3419.                 
  3420.     // Another part is trying to record a sound while we 
  3421.     // are currently recording ours.  Deny the request.
  3422.     if ( focus == gGlobals->fSoundInFocus && this->SoundRecording() )
  3423.         willRelinquish = kODFalse;
  3424.                     
  3425.     return willRelinquish;
  3426. }
  3427.  
  3428. //------------------------------------------------------------------------------
  3429. // Method:        CommitRelinquishFocus
  3430. // Origin:        ODPart
  3431. //
  3432. // Description:    This method is called when it is actually time to give
  3433. //                up a focus that had been requested by another part (or
  3434. //                possibly ourself).
  3435. //
  3436. //                The part calls its FocusLost method to handle the 
  3437. //                "reliquishing" of the particular focus.
  3438. //------------------------------------------------------------------------------
  3439.  
  3440. void SoundEditor::CommitRelinquishFocus( Environment*    ev,
  3441.                                          ODTypeToken    focus,
  3442.                                          ODFrame*        ownerFrame,
  3443.                                          ODFrame*        proposedFrame )
  3444. {
  3445.     SOM_Trace("SoundEditor","CommitRelinquishFocus");
  3446.  
  3447.     // We agreed to give up our FocusSet and now we are being asked to do so.
  3448.     this->FocusLost(ev, focus, ownerFrame);
  3449. }
  3450.  
  3451. //------------------------------------------------------------------------------
  3452. // Method:        AbortRelinquishFocus
  3453. // Origin:        ODPart
  3454. //
  3455. // Description:    This method is called when another part (or possibly
  3456. //                ourself) requested a focus for one of its display
  3457. //                frames, but we returned kODFalse from
  3458. //                BeginRelinqishFocus for one, or all, of the requested
  3459. //                focus. At this point, we are being told to resume
  3460. //                ownership of the focus.
  3461. //
  3462. //                The part calls its FocusAcquired method to handle the 
  3463. //                re-"acquisition" of the particular focus.
  3464. //------------------------------------------------------------------------------
  3465.  
  3466. void SoundEditor::AbortRelinquishFocus( Environment*        ev,
  3467.                                         ODTypeToken            focus,
  3468.                                         ODFrame*            ownerFrame,
  3469.                                         ODFrame*            proposedFrame )
  3470. {
  3471.     SOM_Trace("SoundEditor","AbortRelinquishFocus");
  3472.  
  3473.     // Some parts may have suspended some actions in the BeginRelinquishFocus
  3474.     // method. If so, they would resume those actions here.
  3475. }
  3476.  
  3477. //------------------------------------------------------------------------------
  3478. // Method:        FocusAcquired
  3479. // Origin:        ODPart
  3480. //
  3481. // Description:    This method is called when the Arbitrator has
  3482. //                registered us as the "owner" of a particular focus.
  3483. //                This can occur if a focus is transfered to one of the part's 
  3484. //                display frames.
  3485. //
  3486. //                The part will request the other foci it needs to be "active". If
  3487. //                the part is successful, we notify ourself to become active.
  3488. //------------------------------------------------------------------------------
  3489.  
  3490. void SoundEditor::FocusAcquired( Environment*    ev,
  3491.                                  ODTypeToken    focus,
  3492.                                  ODFrame*        ownerFrame )
  3493. {
  3494.     SOM_Trace("SoundEditor","FocusAcquired");
  3495.  
  3496.     ODArbitrator* arbitrator = fSession->GetArbitrator(ev);
  3497.  
  3498.     if ( gGlobals->fUIFocusSet->Contains(ev, focus) )
  3499.     {
  3500.         if ( arbitrator->RequestFocusSet(ev, gGlobals->fUIFocusSet, ownerFrame) )
  3501.             this->PartActivated(ev, ownerFrame);
  3502.     }
  3503.     else if ( focus == gGlobals->fSoundOutFocus )
  3504.     {
  3505.         if ( !fPlayer->Initialize() )
  3506.             this->DoDialogBox(ev, ownerFrame, kErrorBoxID, kErrCannotInitSndChannel);
  3507.     }
  3508.     else if ( focus == gGlobals->fSoundInFocus )
  3509.     {
  3510.         fRecorder->Initialize();
  3511.     }
  3512. }
  3513.  
  3514. //------------------------------------------------------------------------------
  3515. // Method:        FocusLost
  3516. // Origin:        ODPart
  3517. //
  3518. // Description:    This method is called when the Arbitrator has
  3519. //                unregistered us as the "owner" of the particular
  3520. //                focus.
  3521. //
  3522. //                The part unmarks the active frame if the selection
  3523. //                focus is lost.
  3524. //------------------------------------------------------------------------------
  3525.  
  3526. void SoundEditor::FocusLost( Environment*    ev,
  3527.                              ODTypeToken    focus,
  3528.                              ODFrame*        ownerFrame )
  3529. {
  3530.     SOM_Trace("SoundEditor","FocusLost");
  3531.  
  3532.     if ( focus == gGlobals->fSelectionFocus )
  3533.     {
  3534.         CFrameInfo* frameInfo = (CFrameInfo*) ownerFrame->GetPartInfo(ev);
  3535.         frameInfo->SetFrameActive(kODFalse);
  3536.     }
  3537.     else if ( focus == gGlobals->fSoundOutFocus )
  3538.     {
  3539.         fPlayer->DeInitialize();
  3540.     }
  3541.     else if ( focus == gGlobals->fSoundInFocus )
  3542.     {
  3543.         fRecorder->DeInitialize();
  3544.     }
  3545. }
  3546.  
  3547. //------------------------------------------------------------------------------
  3548. // Method:        RelinquishAllFoci
  3549. // Origin:        SoundEditor
  3550. //
  3551. // Description:    This method is called when a frame is going away. The method
  3552. //                relinquishes all foci the frame owns.
  3553. //------------------------------------------------------------------------------
  3554.  
  3555. void SoundEditor::RelinquishAllFoci( Environment*    ev,
  3556.                                      ODFrame*        frame )
  3557. {
  3558.     SOM_Trace("SoundEditor","RelinquishAllFoci");
  3559.  
  3560.     ODArbitrator* arbitrator = fSession->GetArbitrator(ev);
  3561.     
  3562.     // Make sure the frame going away does own any foci. Forgetting
  3563.     // to do this, will cause a "refcounting" error when the frame
  3564.     // is deleted by the draft.    
  3565.     TRY
  3566.         TempODFrame focusFrame = arbitrator->AcquireFocusOwner(ev, gGlobals->fSelectionFocus);
  3567.         if ( ODObjectsAreEqual(ev, focusFrame, frame) )
  3568.         {
  3569.             arbitrator->RelinquishFocus(ev, gGlobals->fSelectionFocus, frame);
  3570.             this->FocusLost(ev, gGlobals->fSelectionFocus, frame);
  3571.         }
  3572.     CATCH_ALL
  3573.     ENDTRY    
  3574.  
  3575.     TRY
  3576.         TempODFrame focusFrame = arbitrator->AcquireFocusOwner(ev, gGlobals->fMenuFocus);
  3577.         if ( ODObjectsAreEqual(ev, focusFrame, frame) )
  3578.         {
  3579.             arbitrator->RelinquishFocus(ev, gGlobals->fMenuFocus, frame);
  3580.             this->FocusLost(ev, gGlobals->fMenuFocus, frame);
  3581.         }
  3582.     CATCH_ALL
  3583.     ENDTRY    
  3584.  
  3585.     TRY
  3586.         TempODFrame focusFrame = arbitrator->AcquireFocusOwner(ev, gGlobals->fKeyFocus);
  3587.         if ( ODObjectsAreEqual(ev, focusFrame, frame) )
  3588.         {
  3589.             arbitrator->RelinquishFocus(ev, gGlobals->fKeyFocus, frame);
  3590.             this->FocusLost(ev, gGlobals->fKeyFocus, frame);
  3591.         }
  3592.     CATCH_ALL
  3593.     ENDTRY    
  3594.  
  3595.     TRY
  3596.         TempODFrame focusFrame = arbitrator->AcquireFocusOwner(ev, gGlobals->fSoundOutFocus);
  3597.         if ( ODObjectsAreEqual(ev, focusFrame, frame) )
  3598.         {
  3599.             arbitrator->RelinquishFocus(ev, gGlobals->fSoundOutFocus, frame);
  3600.             this->FocusLost(ev, gGlobals->fSoundOutFocus, frame);
  3601.         }
  3602.     CATCH_ALL
  3603.     ENDTRY    
  3604.  
  3605.     TRY
  3606.         TempODFrame focusFrame = arbitrator->AcquireFocusOwner(ev, gGlobals->fSoundInFocus);
  3607.         if ( ODObjectsAreEqual(ev, focusFrame, frame) )
  3608.         {
  3609.             arbitrator->RelinquishFocus(ev, gGlobals->fSoundInFocus, frame);
  3610.             this->FocusLost(ev, gGlobals->fSoundInFocus, frame);
  3611.         }
  3612.     CATCH_ALL
  3613.     ENDTRY    
  3614. }
  3615.  
  3616. //------------------------------------------------------------------------------
  3617. // Method:        PartActivated
  3618. // Origin:        SoundEditor
  3619. //
  3620. // Description:    This method is called when the part gets activated (by
  3621. //                asking for the contents of its focus set or having focus
  3622. //                transferred to it).
  3623. //------------------------------------------------------------------------------
  3624.  
  3625. void SoundEditor::PartActivated(     Environment*    ev,
  3626.                                     ODFrame*        frame )
  3627. {
  3628.     SOM_Trace("SoundEditor","PartActivated");
  3629.         
  3630.     // We are required to re-validate the menubar before displaying it because
  3631.     // any part can/could swap the base menubar at any time.
  3632.     if ( !gGlobals->fMenuBar->IsValid(ev) )
  3633.         this->BuildMenuBar(ev);
  3634.         
  3635.     // Display our menu bar.
  3636.     gGlobals->fMenuBar->Display(ev);
  3637.  
  3638.     // And set our "active" state.
  3639.     CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  3640.     frameInfo->SetFrameActive(kODTrue);
  3641. }
  3642.  
  3643. //------------------------------------------------------------------------------
  3644. // Method:        ActivateFrame
  3645. // Origin:        SoundEditor
  3646. //
  3647. // Description:    This method is called by the part when a kODEvtMouseUp
  3648. //                occurs in an inactive frame in an active window, and
  3649. //                when an OpenDoc document comes forward.
  3650. //
  3651. //                The part activates the frame by requesting the
  3652. //                UIFocusSet (created in Initialize) and by calling
  3653. //                FocusAcquired if we were successful. The method
  3654. //                returns true if no problems were encountered as a
  3655. //                signal to the caller that the frame is now "active".
  3656. //------------------------------------------------------------------------------
  3657.  
  3658. ODBoolean SoundEditor::ActivateFrame( Environment*    ev,
  3659.                                      ODFrame*        frame )
  3660. {
  3661.     SOM_Trace("SoundEditor","ActivateFrame");
  3662.  
  3663.     ODBoolean activated = kODFalse;
  3664.  
  3665.     // Request the set of foci necessary to become active.
  3666.     if ( fSession->GetArbitrator(ev)
  3667.                 ->RequestFocusSet(ev, gGlobals->fUIFocusSet, frame) )
  3668.     {
  3669.         // Activate the part.
  3670.         this->PartActivated(ev, frame);
  3671.         // We were able to become active.
  3672.         activated = kODTrue;
  3673.     }
  3674.     
  3675.     return activated;
  3676. }
  3677.  
  3678. //------------------------------------------------------------------------------
  3679. // Method:        WindowActivating
  3680. // Origin:        SoundEditor
  3681. //
  3682. // Description:    This method is called by the part when a window activation
  3683. //                event (kODEvtActivate) occurs.
  3684. //
  3685. //                The part remembers the frame's active state and restores it when
  3686. //                events come in. If the frame is active and the window is being
  3687. //                sent into the background, remember to reactivate the part when
  3688. //                the window is reactivated.
  3689. //------------------------------------------------------------------------------
  3690.  
  3691. void SoundEditor::WindowActivating( Environment*    ev,
  3692.                                     ODFrame*        frame,
  3693.                                     ODBoolean        activating )
  3694. {
  3695.     SOM_Trace("SoundEditor","WindowActivating");
  3696.  
  3697.     CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  3698.     
  3699.     // The window is being activated and we had the selection focus when
  3700.     // the window was deactivated, so request it again.
  3701.     if ( activating && frameInfo->FrameNeedsReactivating() )
  3702.     {
  3703.         this->ActivateFrame(ev, frame);
  3704.         frameInfo->SetFrameReactivate(kODFalse);
  3705.     }
  3706.     // The window is being deactivated and we have the active part.
  3707.     // So give up our foci and remind ourselves that we need to request
  3708.     // the focus when the window is reactivated.
  3709.     else if ( !activating && frameInfo->IsFrameActive() )
  3710.     {
  3711.         frameInfo->SetFrameReactivate(kODTrue);
  3712.     }
  3713. }
  3714.  
  3715. //==============================================================================
  3716. #pragma mark    • Event Handling •
  3717. //==============================================================================
  3718.  
  3719. //------------------------------------------------------------------------------
  3720. // Method:        HandleEvent
  3721. // Origin:        ODPart
  3722. //
  3723. // Description:    The method is called when an event, which falls into
  3724. //                the category of an owned focus, occurs. The two
  3725. //                exceptions to this are "mouse movement" events and 
  3726. //                embedded frame events, which can occur when a part
  3727. //                owns no foci.
  3728. //
  3729. //                The part returns true if the event was handled.
  3730. //------------------------------------------------------------------------------
  3731.  
  3732.  
  3733. ODBoolean SoundEditor::HandleEvent( Environment*        ev,
  3734.                                     ODEventData*        event,
  3735.                                     ODFrame*            frame,
  3736.                                     ODFacet*            facet,
  3737.                                     ODEventInfo*        eventInfo )
  3738. {
  3739.     SOM_Trace("SoundEditor","HandleEvent");
  3740.  
  3741.     ODBoolean    eventHandled = kODFalse;
  3742.  
  3743.     // Event handling is basically the same as standard Macintosh applications,
  3744.     // except that the events have been renamed for cross-platform compatability.
  3745.     
  3746.     switch ( event->what )
  3747.     {
  3748.         case kODEvtNull:
  3749.             if ( this->SoundDonePlaying(ev) )
  3750.             {
  3751.                 // if the length was unknown before playing
  3752.                 // we now know the length of the sound if it
  3753.                 // stopped because it ended.
  3754.                 if ( fUnknownSound )
  3755.                     fSecsRecorded = -fSecsRecorded;
  3756.                     
  3757.                 this->StopPlayingSound(ev);
  3758.             }
  3759.             if ( this->SoundDoneRecording(ev) )
  3760.                 this->StopRecordingSound(ev);
  3761.             eventHandled = kODTrue;
  3762.             break;
  3763.  
  3764.         case kODEvtMouseDown:
  3765.         case kODEvtMouseUp:
  3766.             eventHandled = this->HandleMouseEvent(ev, event, facet, eventInfo);
  3767.             break;
  3768.             
  3769.         case kODEvtMenu:
  3770.             eventHandled = this->HandleMenuEvent(ev, event, frame);
  3771.             break;
  3772.  
  3773.         case kODEvtKeyDown:
  3774.             eventHandled = this->HandleKeyboardEvent(ev, event, frame);
  3775.             break;
  3776.             
  3777.         case kODEvtActivate:
  3778.             // We are being notified that a window we are displayed in has
  3779.             // just been de/activated (un/hilighted).
  3780.             this->WindowActivating(ev, frame, (event->modifiers & activeFlag));
  3781.             eventHandled = kODTrue;
  3782.             break;
  3783.     
  3784.         case kODEvtMouseEnter:
  3785.         case kODEvtMouseLeave:
  3786.             SetCursor(&ODQDGlobals.arrow);
  3787.             eventHandled = kODTrue;
  3788.             break;
  3789.  
  3790.         case kODEvtMouseWithin:
  3791.             eventHandled = kODTrue;
  3792.             break;
  3793.  
  3794.         // Other events a part might handle
  3795.         case kODEvtMouseDownEmbedded:
  3796.         case kODEvtMouseUpEmbedded:
  3797.         case kODEvtMouseDownBorder:
  3798.         case kODEvtMouseUpBorder:
  3799.         case kODEvtWindow:
  3800.         case kODEvtKeyUp:
  3801.         case kODEvtAutoKey:
  3802.         case kODEvtDisk:
  3803.         case kODEvtOS:
  3804.         
  3805.         default:
  3806.             break;
  3807.     }
  3808.  
  3809.     return eventHandled;
  3810. }
  3811.  
  3812. //------------------------------------------------------------------------------
  3813. // Method:        BuildMenuBar
  3814. // Origin:        SoundEditor
  3815. //
  3816. // Description:    This method is called when the menubar needs to be created or
  3817. //                changed (because it has become invalid).
  3818. //------------------------------------------------------------------------------
  3819.  
  3820. void SoundEditor::BuildMenuBar( Environment* ev )
  3821. {
  3822.     if (gGlobals->fMenuBar == kODNULL || !gGlobals->fMenuBar->IsValid(ev))
  3823.     {
  3824.         ODBoolean changingBaseMenuBar = (gGlobals->fMenuBar != kODNULL);
  3825.         
  3826.         if (changingBaseMenuBar)
  3827.         {
  3828.             // Remove our menus from the old menubar.
  3829.             for (ODSShort index = 0; index < kNumMenus; index++)
  3830.             {
  3831.                 ODSShort id = kBaseMenuID + index;
  3832.                 gGlobals->fMenuBar->RemoveMenu(ev, id);
  3833.             }
  3834.         }
  3835.         
  3836.         ODFinalReleaseObject(ev, gGlobals->fMenuBar);
  3837.  
  3838.         // It is required that parts instantiate their menu bars from 
  3839.         // the base OpenDoc menu bar. This maintains consistency in the
  3840.         // default menu items and their placement.
  3841.         // Since the object is a copy, we can add and subtract menus and
  3842.         // items without affecting other running parts.
  3843.         gGlobals->fMenuBar = fSession->GetWindowState(ev)->CopyBaseMenuBar(ev);
  3844.                 
  3845.         // Add our resource-based menus and register all the editor specific
  3846.         // command numbers.
  3847.         CUsingLibraryResources res;
  3848.         for (ODSShort index = 0; index < kNumMenus; index++)
  3849.         {
  3850.             ODSShort id = kBaseMenuID + index;
  3851.             
  3852.             MenuHandle menu = GetMenu(id);
  3853.             if (menu != kODNULL)
  3854.                 gGlobals->fMenuBar->AddMenuLast(ev, id, menu, fSelf);
  3855.         }
  3856.         
  3857.         gGlobals->fMenuBar->RegisterCommand(ev, kPlayCommand,     kSoundMenuID, 1);
  3858.         gGlobals->fMenuBar->RegisterCommand(ev, kPauseCommand,    kSoundMenuID, 2);
  3859.         gGlobals->fMenuBar->RegisterCommand(ev, kStopCommand,     kSoundMenuID, 3);
  3860.         gGlobals->fMenuBar->RegisterCommand(ev, kRecordCommand,   kSoundMenuID, 4);
  3861.         gGlobals->fMenuBar->RegisterCommand(ev, kSettingsCommand, kSoundMenuID, 6);
  3862.     }
  3863. }
  3864.  
  3865. //------------------------------------------------------------------------------
  3866. // Method:        AdjustMenus
  3867. // Origin:        ODPart
  3868. //
  3869. // Description:    This method is called when a kODEvtMouseDown event
  3870. //                occurs in the menubar and the part owns the "menu"
  3871. //                focus or it is the root part of the window with the active
  3872. //                part.
  3873. //
  3874. //                The part enables the appropriate items for its current
  3875. //                state and updates the menu items to reflect this
  3876. //                editor.
  3877. //------------------------------------------------------------------------------
  3878.  
  3879. void SoundEditor::AdjustMenus( Environment*        ev,
  3880.                                ODFrame*            frame )
  3881. {
  3882.     SOM_Trace("SoundEditor","AdjustMenus");
  3883.  
  3884.     ODArbitrator* arbitrator = fSession->GetArbitrator(ev);
  3885.  
  3886.     // The menubar object always calls the root part's AdjustMenus method before
  3887.     // calling the menu focus owner's. Because of this, we need to validate the
  3888.     // menubar in the case where we are the root part.
  3889.     if ( frame->IsRoot(ev) )
  3890.     {
  3891.         // We are required to re-validate the menubar before displaying it because
  3892.         // any part can/could swap the base menubar at any time.
  3893.         if ( !gGlobals->fMenuBar->IsValid(ev) )
  3894.             this->BuildMenuBar(ev);
  3895.     }        
  3896.  
  3897.     // En/disable the stop and play commands accordingly.
  3898.     // Also check/uncheck the pause menu item
  3899.  
  3900.     gGlobals->fMenuBar->EnableCommand(ev, kPlayCommand,   this->IsPlayAvailable());
  3901.     gGlobals->fMenuBar->CheckCommand(ev,  kPlayCommand,   this->SoundPlaying());
  3902.                                                 
  3903.     gGlobals->fMenuBar->EnableCommand(ev, kPauseCommand,  this->IsPauseAvailable());
  3904.     gGlobals->fMenuBar->CheckCommand(ev,  kPauseCommand,  this->SoundPaused());
  3905.  
  3906.     gGlobals->fMenuBar->EnableCommand(ev, kStopCommand,   this->IsStopAvailable());
  3907.  
  3908.     gGlobals->fMenuBar->EnableCommand(ev, kRecordCommand, this->IsRecordAvailable());
  3909.     gGlobals->fMenuBar->CheckCommand(ev,  kRecordCommand, this->SoundRecording());
  3910.     
  3911.     // En/disable the edit commands accordingly.
  3912.     
  3913.     ODBoolean isBusy = (this->SoundPlaying() || this->SoundRecording());
  3914.     ODBoolean canCopy = (fPlayer->HasSound() && !isBusy);
  3915.     gGlobals->fMenuBar->EnableCommand(ev, kODCommandCopy, canCopy);
  3916.     
  3917.     ODBoolean canCut = (canCopy && !fReadOnlyStorage);
  3918.     gGlobals->fMenuBar->EnableCommand(ev, kODCommandCut,   canCut);
  3919.     gGlobals->fMenuBar->EnableCommand(ev, kODCommandClear, canCut);
  3920.  
  3921.     TRY
  3922.         ODBoolean canPaste = ((!fReadOnlyStorage && this->SoundOnClipboard(ev, frame)) 
  3923.                               && !isBusy);
  3924.         gGlobals->fMenuBar->EnableCommand(ev, kODCommandPaste, canPaste);
  3925.     CATCH_ALL
  3926.         // Consume exception.
  3927.     ENDTRY
  3928.     
  3929.     // Enable the "Settings" command.
  3930.     gGlobals->fMenuBar->EnableCommand(ev, kSettingsCommand, !this->SoundRecording());
  3931.  
  3932.     // Enable the "View As Window" command.
  3933.     gGlobals->fMenuBar->EnableCommand(ev, kODCommandViewAsWin, !frame->IsRoot(ev));
  3934.     
  3935.     // Load menu item names.
  3936.     TRY
  3937.         ODArbitrator* arbitrator = fSession->GetArbitrator(ev);
  3938.         TempODFrame menuOwner = arbitrator->AcquireFocusOwner(ev, gGlobals->fMenuFocus);
  3939.         
  3940.         // There is no need to load a resource, create an ODIText object, set the menubar,
  3941.         // and cleanup, if we don't own the menu focus.
  3942.         if ( ODObjectsAreEqual(ev, frame, menuOwner) )
  3943.         {
  3944.             // Change the "About" menu item text for our part.
  3945.             Str63 text;
  3946.             ODGetIndString(text, kMenuStringResID, kAboutTextID);
  3947.             
  3948.             TempODIText menuItem = CreateIText(gGlobals->fEditorsScript, 
  3949.                                                gGlobals->fEditorsLanguage,
  3950.                                                (StringPtr) &text);
  3951.             gGlobals->fMenuBar->SetItemString(ev, kODCommandAbout, menuItem);
  3952.  
  3953.             // Set the resource file so our balloon help menu items will appear.
  3954.             WASSERT(fMenuSavedResFile == -1);
  3955.             fMenuSavedResFile = BeginUsingLibraryResources();
  3956.         }
  3957.     CATCH_ALL
  3958.         // Consume exception.
  3959.     ENDTRY
  3960. }
  3961.  
  3962. //------------------------------------------------------------------------------
  3963. // Method:        HandleMenuEvent
  3964. // Origin:        SoundEditor
  3965. //
  3966. // Description:    This method is called by the part when a menu event is
  3967. //                received.
  3968. //
  3969. //                Using the menu bar object, we determine what the menu
  3970. //                command is, and call the appropriate method to handle it.
  3971. //------------------------------------------------------------------------------
  3972.  
  3973. ODBoolean SoundEditor::HandleMenuEvent( Environment*    ev,
  3974.                                         ODEventData*    event,
  3975.                                         ODFrame*        frame )
  3976. {
  3977.     SOM_Trace("SoundEditor","HandleMenuEvent");
  3978.  
  3979.     // Restore the previous resource file.
  3980.     if ( fMenuSavedResFile != -1 )
  3981.     {
  3982.         EndUsingLibraryResources(fMenuSavedResFile);
  3983.         fMenuSavedResFile = -1;
  3984.     }
  3985.     
  3986.     ODULong        menuResult    = event->message;
  3987.     ODUShort    menu        = HiWord(menuResult);
  3988.     ODUShort    item        = LoWord(menuResult);
  3989.     ODCommandID    command        = gGlobals->fMenuBar->GetCommand(ev, menu, item);
  3990.  
  3991.     if ( this->HandleSoundEvent(ev, command) )
  3992.         return kODTrue;
  3993.         
  3994.     switch ( command )
  3995.     {
  3996.         case kSettingsCommand:
  3997.             this->ShowSettingsDialog(ev, frame);
  3998.             break;
  3999.  
  4000.         // --- OpenDoc commands ---
  4001.  
  4002.         case kODCommandAbout:    
  4003.             this->DoDialogBox(ev, frame, kAboutBoxID);
  4004.             break;
  4005.  
  4006.         case kODCommandViewAsWin:
  4007.             this->Open(ev, frame);
  4008.             break;
  4009.  
  4010.         case kODCommandCut:
  4011.         case kODCommandCopy:
  4012.             this->CopySound(ev, frame);
  4013.             this->AnnotateClipboard(ev, frame);
  4014.             
  4015.             if ( command == kODCommandCut )
  4016.                 this->ClearSound(ev, frame);
  4017.             break;
  4018.  
  4019.         case kODCommandPaste:
  4020.             this->PasteSound(ev, frame);
  4021.             break;
  4022.  
  4023.         case kODCommandClear:
  4024.             this->ClearSound(ev, frame);
  4025.             break;
  4026.  
  4027.         // Other commands a part might handle
  4028.         case kODCommandOpen:
  4029.         case kODCommandInsert:
  4030.         case kODCommandPageSetup:
  4031.         case kODCommandPrint:
  4032.         case kODCommandUndo:
  4033.         case kODCommandRedo:
  4034.         case kODCommandPasteAs:
  4035.         case kODCommandSelectAll:
  4036.         case kODCommandGetPartInfo:
  4037.         case kODCommandPreferences:
  4038.  
  4039.         default:
  4040.             return kODFalse;
  4041.     }
  4042.         
  4043.     return kODTrue;
  4044. }
  4045.  
  4046.  
  4047.  
  4048. //--------------------------------------------------------------------
  4049. // Method:        HandleKeyboardEvent
  4050. // Origin:        SoundEditor
  4051. //
  4052. // Description:    This method is called by the part when a keyboard
  4053. //                event is recieved.
  4054. //
  4055. //                The editor supports playing the recorded sound by
  4056. //                hitting the space bar.
  4057. //-------------------------------------------------------------------------------
  4058.  
  4059. ODBoolean SoundEditor::HandleKeyboardEvent( Environment*    ev,
  4060.                                             ODEventData*    event,
  4061.                                             ODFrame*        frame )
  4062. {
  4063.     const long kSpaceBar = 0x20L;
  4064.  
  4065.     SOM_Trace("SoundEditor","HandleKeyboardEvent");
  4066.  
  4067.     ODBoolean handled = kODFalse;
  4068.  
  4069.     // If the controls palette is showing and the user hits the space
  4070.     // bar, stop playing the sound if something is playing,
  4071.     //         stop recording a sound if something is recording,
  4072.     //        or start playing the sound otherwise
  4073.     
  4074.     if ( frame->GetViewType(ev) == gGlobals->fFrameView &&
  4075.          frame->GetPresentation(ev) == gGlobals->fControlsPresentation )
  4076.     {
  4077.         if ( (event->message & charCodeMask) == kSpaceBar )
  4078.         {
  4079.             if ( this->SoundPlaying() )
  4080.                 this->StopPlayingSound(ev);
  4081.             
  4082.             else if ( this->SoundRecording() )
  4083.                 this->StopRecordingSound(ev);
  4084.             
  4085.             else
  4086.                 this->PlaySound(ev);
  4087.  
  4088.             handled = kODTrue;
  4089.         }
  4090.     }
  4091.  
  4092.     return handled;
  4093. }
  4094.  
  4095. //------------------------------------------------------------------------------
  4096. // Method:        HandleMouseEvent
  4097. // Origin:        SoundEditor
  4098. //
  4099. // Description:    This method is called by the part when a mouse event
  4100. //                is recieved.
  4101. //
  4102. //                A typical part would determine the event type(up/down)
  4103. //                and respond appropriately, but we have no "real"
  4104. //                content model, so we activate the part as appropriate
  4105. //                and call a generic method to handle the event.
  4106. //
  4107. // Remember:    When a frame is inactive, the first mouse up event
  4108. //                should activate it; inactive frames do not recieve
  4109. //                kODEvtMouseDown events.
  4110. //------------------------------------------------------------------------------
  4111.  
  4112. ODBoolean SoundEditor::HandleMouseEvent( Environment*    ev,
  4113.                                          ODEventData*    event,
  4114.                                          ODFacet*        facet,
  4115.                                          ODEventInfo*    eventInfo )
  4116. {
  4117.     SOM_Trace("SoundEditor","HandleMouseEvent");
  4118.  
  4119.     // If the facet parameter is invalid, the mouse up occurred outside the
  4120.     // bounds of a Modal window, otherwise it should be treated normally.
  4121.     
  4122.     ODBoolean result = kODTrue;
  4123.     ODFrame* frame = facet->GetFrame(ev);
  4124.  
  4125.     if ( facet != kODNULL )
  4126.     {
  4127.         if ( event->what == kODEvtMouseUp )
  4128.         {
  4129.             // Handle the mouse up event.
  4130.             result = this->DoMouseUp(ev, event, facet, eventInfo);
  4131.         }
  4132.         else if ( event->what == kODEvtMouseDown )                
  4133.         {
  4134.             // Handle the mouse down event.
  4135.             this->DoMouseDown(ev, event, facet, eventInfo);
  4136.         }
  4137.     }
  4138.     else
  4139.     {
  4140.         // User clicked outside the bounds of a Modal window.        
  4141.         SysBeep(1);
  4142.     }
  4143.  
  4144.     return result;
  4145. }
  4146.  
  4147. //------------------------------------------------------------------------------
  4148. // Method:        DoMouseUp
  4149. // Origin:        SoundEditor
  4150. //
  4151. // Description:    This method is called by the part when a mouse event
  4152. //                is recieved.
  4153. //
  4154. //                If the user mouses up in the sound controls palette
  4155. //                over an active button, perform the action (record,
  4156. //                play, stop).
  4157. //------------------------------------------------------------------------------
  4158.  
  4159. ODBoolean SoundEditor::DoMouseUp( Environment*        ev,
  4160.                                   ODEventData*        event,
  4161.                                   ODFacet*            facet,
  4162.                                   ODEventInfo*        eventInfo )
  4163. {
  4164.     SOM_Trace("SoundEditor","DoMouseUp");
  4165.  
  4166.     ODWindow* window = facet->GetWindow(ev);
  4167.  
  4168.     if ( !window->IsActive(ev) )
  4169.     {
  4170.         // Activate inactive windows on the first mouse up event.
  4171.         window->Select(ev);
  4172.     }
  4173.     else
  4174.     {
  4175.         // Activate the frame (if needed) on all subsequent mouse up events.
  4176.         ODFrame* frame = facet->GetFrame(ev);
  4177.     
  4178.         // Get our state information from the PartInfo of the frame.
  4179.         CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
  4180.             
  4181.         // If this frame is not the active one, activate it by requesting
  4182.         // the appropriate foci.
  4183.  
  4184.         if ( !frameInfo->IsFrameActive() )
  4185.         {
  4186.             if ( this->ActivateFrame(ev, frame) )
  4187.                 // Keep track of which facet was the last active for positioning
  4188.                 // part windows.
  4189.                 frameInfo->SetActiveFacet(facet);
  4190.             else
  4191.                 // We were unable to acquire the necessary focus and activate it.
  4192.                 return kODFalse;
  4193.         }
  4194.     
  4195.         // No do any other action that would be appropriate for a mouse up
  4196.         // event.
  4197.     }
  4198.  
  4199.     return kODTrue;
  4200. }
  4201.  
  4202. //------------------------------------------------------------------------------
  4203. // Method:        DoMouseDown
  4204. // Origin:        SoundEditor
  4205. //
  4206. // Description:    This method is called by the part when a mouse event
  4207. //                is recieved.
  4208. //
  4209. //                If the facet is displaying the sound controls palette,
  4210. //                we handle mouse downs on the buttons.
  4211. //------------------------------------------------------------------------------
  4212.  
  4213. void SoundEditor::DoMouseDown( Environment*        ev,
  4214.                                ODEventData*        event,
  4215.                                ODFacet*            facet,
  4216.                                ODEventInfo*        eventInfo )
  4217. {
  4218.     SOM_Trace("SoundEditor","DoMouseDown");
  4219.     
  4220.     EButton        button = kNoButton;
  4221.     ODFrame*    frame = facet->GetFrame(ev);
  4222.  
  4223.     // If the mouse down occurred in a active frame in an active
  4224.     // window, see if the control palette is displayed and 
  4225.     // respond to the click; otherwise, just ignore the event.
  4226.  
  4227.     if ( frame->GetViewType(ev) == gGlobals->fFrameView )
  4228.     {
  4229.         EButtonState initialState = kButtonStateNormal;
  4230.     
  4231.         Point where;
  4232.  
  4233.         // Get the localized mouse coordinates from the Event Info.
  4234.         where.h = FixedToInt(eventInfo->where.x);
  4235.         where.v = FixedToInt(eventInfo->where.y);
  4236.  
  4237.         // Record button is only disabled when there
  4238.         // is something recording and not paused
  4239.         // or if the draft is read-only
  4240.         if ( PtInRect(where, &gGlobals->fButtonRect[kRecordButton]) 
  4241.            && this->IsRecordAvailable() )
  4242.         {
  4243.             button = kRecordButton;
  4244.             if ( this->SoundRecording() )
  4245.                 initialState = kButtonStateOn;
  4246.         }
  4247.         
  4248.         // Stop button is only enabled when there
  4249.         // is something to stop (record or play)
  4250.         else if ( PtInRect(where, &gGlobals->fButtonRect[kStopButton])
  4251.                 && this->IsStopAvailable() )
  4252.         {
  4253.             button = kStopButton;
  4254.         }
  4255.         
  4256.         // Pause button is only enabled when there
  4257.         // is something to pause (record or play)
  4258.         else if ( PtInRect(where, &gGlobals->fButtonRect[kPauseButton]) 
  4259.                 && this->IsPauseAvailable() )
  4260.         {
  4261.             button = kPauseButton;
  4262.             if ( this->SoundPaused() )
  4263.                 initialState = kButtonStateOn;
  4264.         }
  4265.         
  4266.         // Play button is only enabled when there is something playing and paused,
  4267.         // or if nothing is playing or recording
  4268.         else if ( PtInRect(where, &gGlobals->fButtonRect[kPlayButton])
  4269.                && this->IsPlayAvailable() )
  4270.         {
  4271.             button = kPlayButton;
  4272.             if ( this->SoundPlaying() )
  4273.                 initialState = kButtonStateOn;
  4274.         }
  4275.                     
  4276.         CUsingLibraryResources res;
  4277.  
  4278.         // Handle tracking if a button was hit.
  4279.         CFocus initiateDrawing(ev, facet);
  4280.         if (button != kNoButton && this->TrackButton(ev, button, where, initialState)) 
  4281.         {
  4282.             ODFrame* frame = facet->GetFrame(ev);
  4283.         
  4284.             // Handle clicking in palette buttons
  4285.             
  4286.             switch (button)
  4287.             {
  4288.                 case kRecordButton:
  4289.                     this->HandleSoundEvent(ev, kRecordCommand);
  4290.                     break;
  4291.  
  4292.                 case kStopButton:
  4293.                     this->HandleSoundEvent(ev, kStopCommand);
  4294.                     break;
  4295.  
  4296.                 case kPlayButton:
  4297.                     this->HandleSoundEvent(ev, kPlayCommand);
  4298.                     break;
  4299.                     
  4300.                 case kPauseButton:
  4301.                     this->HandleSoundEvent(ev, kPauseCommand);
  4302.                     break;
  4303.             }
  4304.                     
  4305.             // Invalidate the button that was pressed.
  4306.             // Change the button appearance to button down or up (not pressed)
  4307.             
  4308.             this->InvalidateButton(ev, button);
  4309.         }
  4310.         else    // Mouse released outside button or button not hit.
  4311.         {
  4312.             if ( button == kPauseButton || button == kPlayButton )
  4313.                 this->InvalidateButton(ev, button);
  4314.                     // This is done because the track call will leave the button
  4315.                     // in an up state if it was tracked out of, but in this case,
  4316.                     // the pause button may have been left on.
  4317.         }
  4318.     }
  4319. }
  4320.  
  4321. //------------------------------------------------------------------------------
  4322. // Method:        HandleSoundEvent
  4323. // Origin:        SoundEditor
  4324. //
  4325. // Description:    This method is called by the part to handle window events.
  4326. //------------------------------------------------------------------------------
  4327.  
  4328. ODBoolean SoundEditor::HandleSoundEvent( Environment*    ev, 
  4329.                                          ODCommandID    command )
  4330. {
  4331.     switch ( command )
  4332.     {
  4333.         case kPlayCommand:
  4334.             if ( fPlayer->IsPaused() )
  4335.                 this->ResumePlayingSound(ev);
  4336.             else
  4337.                 this->PlaySound(ev);
  4338.             break;
  4339.             
  4340.         case kPauseCommand:
  4341.             if ( this->SoundPlaying() )
  4342.             {
  4343.                 if ( fPlayer->IsPaused() )
  4344.                     this->ResumePlayingSound(ev);
  4345.                 else
  4346.                     this->PausePlayingSound(ev);
  4347.             }
  4348.             
  4349.             if ( this->SoundRecording() )
  4350.             {
  4351.                 if ( fRecorder->IsPaused() )
  4352.                     this->ResumeRecordingSound(ev);
  4353.                 else
  4354.                     this->PauseRecordingSound(ev);
  4355.             }
  4356.             break;
  4357.             
  4358.         case kStopCommand:
  4359.             if ( this->SoundPlaying() )
  4360.                 this->StopPlayingSound(ev);
  4361.         
  4362.             if ( this->SoundRecording() )
  4363.                 this->StopRecordingSound(ev);
  4364.             break;
  4365.             
  4366.         case kRecordCommand:
  4367.             if ( this->SoundRecording() && fRecorder->IsPaused() )
  4368.                 this->ResumeRecordingSound(ev);
  4369.             else if ( !fReadOnlyStorage )
  4370.                 this->RecordSound(ev);
  4371.             break;
  4372.             
  4373.         default:
  4374.             return kODFalse;
  4375.     }
  4376.     
  4377.     return kODTrue;
  4378. }
  4379.  
  4380. //------------------------------------------------------------------------------
  4381. // Method:        DoDialogBox
  4382. // Origin:        SoundEditor
  4383. //
  4384. // Description:    This method is called by the part when a dialog needs to be
  4385. //                displayed (e.g. the About Box). If a valid error number is 
  4386. //                passed in, an error dialog will be displayed.
  4387. //------------------------------------------------------------------------------
  4388.  
  4389. void SoundEditor::DoDialogBox( Environment*        ev,
  4390.                                ODFrame*            frame,
  4391.                                ODSShort            dialogID,
  4392.                                ODUShort            errorNumber )
  4393. {
  4394.     SOM_Trace("SoundEditor","DoDialogBox");
  4395.  
  4396.     // If the calling method does not have a frame available to it, we need to
  4397.     // locate a frame to use for requesting the modal focus. Find the first valid
  4398.     // frame in our display frames list.
  4399.     ODFrame* focusFrame = frame;
  4400.     if ( focusFrame == kODNULL )
  4401.         focusFrame = this->FindFirstAvailableFrame(ev);
  4402.     
  4403.     // Our dialog boxes are modal so we must request the Modal focus to prevent
  4404.     // multiple modal dialogs being displayed simultaneously.
  4405.     
  4406.     TempFocus modalFocus(ev, gGlobals->fModalFocus, focusFrame);
  4407.     if ( modalFocus.Request() )
  4408.     {
  4409.         // Dim the frontmost document window.
  4410.         fSession->GetWindowState(ev)->DeactivateFrontWindows(ev);
  4411.         
  4412.         CUsingLibraryResources res;
  4413.         
  4414.         DialogPtr dialog = GetNewDialog(dialogID, kODNULL, (WindowPtr) -1L);
  4415.         if ( dialog )
  4416.         {
  4417.             if ( errorNumber > 0 )
  4418.             {
  4419.                 Handle    itemHandle;
  4420.                 Rect    itemRect;
  4421.                 short    itemType;
  4422.                 Str255    errStr;
  4423.                 
  4424.                 ODGetIndString(errStr, kErrorStringResID, errorNumber);
  4425.                 GetDialogItem(dialog, kErrStrFieldID, &itemType, &itemHandle, &itemRect);
  4426.                 SetDialogItemText(itemHandle, errStr);
  4427.  
  4428.                 // We don't need the cancel button for an error dialog.
  4429.                 HideDialogItem(dialog, cancel);
  4430.  
  4431.                 SetDialogDefaultItem(dialog, ok);
  4432.             }
  4433.             
  4434.             ShowWindow(dialog);
  4435.             SetCursor(&ODQDGlobals.arrow);
  4436.  
  4437.             ODSShort itemHit;
  4438.             ModalDialog(kODNULL, &itemHit);
  4439.  
  4440.             DisposeDialog(dialog);
  4441.         }
  4442.         else
  4443.         {
  4444.             // Could not load dialog... something is amiss.
  4445.             SysBeep(2);
  4446.         }
  4447.                 
  4448.         // Hilite the frontmost document window.
  4449.         fSession->GetWindowState(ev)->ActivateFrontWindows(ev);
  4450.     }
  4451.     else
  4452.     {
  4453.         // If we can't get the modal focus, then another modal dialog is
  4454.         // already being displayed.
  4455.         SysBeep(2);
  4456.     }
  4457. }
  4458.  
  4459. //==============================================================================
  4460. #pragma mark    • Extensions •
  4461. //==============================================================================
  4462.  
  4463. //------------------------------------------------------------------------------
  4464. // Method:        HasExtension
  4465. // Origin:        ODPart
  4466. //
  4467. // Description:    The method is called when something wants to know if this
  4468. //                part supports a particular type of extension.
  4469. //------------------------------------------------------------------------------
  4470.  
  4471. ODBoolean SoundEditor::HasExtension( Environment*    ev,
  4472.                                      ODType            extensionName )
  4473. {
  4474.     SOM_Trace("SoundEditor","HasExtension");
  4475.     
  4476.     return ( ODISOStrEqual(extensionName, kODSettingsExtension) 
  4477.           || ODISOStrEqual(extensionName, kODExtSemanticInterface) );
  4478. }
  4479.  
  4480. //------------------------------------------------------------------------------
  4481. // Method:        AcquireExtension
  4482. // Origin:        ODPart
  4483. //
  4484. // Description:    The method is called when the part is being asked for
  4485. //                a particular extension.
  4486. //------------------------------------------------------------------------------
  4487.  
  4488. ODExtension* SoundEditor::AcquireExtension( Environment*    ev,
  4489.                                             ODType            extensionName )
  4490. {
  4491.     SOM_Trace("SoundEditor","AcquireExtension");
  4492.  
  4493.     if ( ODISOStrEqual(extensionName, kODSettingsExtension) )
  4494.     {
  4495.         if ( fSettingsExtension == kODNULL )
  4496.         {
  4497.             fSettingsExtension = new som_SESettingsExtension;
  4498.             THROW_IF_NULL(fSettingsExtension);
  4499.             fSettingsExtension->InitSettingsExtension(ev, fSelf);
  4500.         }
  4501.  
  4502.         ODAcquireObject(ev, fSettingsExtension);
  4503.         return fSettingsExtension;
  4504.     }
  4505.     else if ( ODISOStrEqual(extensionName, kODExtSemanticInterface) )
  4506.     {
  4507.         if ( fSemIntf == kODNULL )
  4508.         {
  4509.             // Instantiate the semantic interface extension and helper objects
  4510.             fSemIntf = new SoundEditorSI;
  4511.             THROW_IF_NULL(fSemIntf);
  4512.             fSemIntf->InitSemanticInterface(ev, fSelf, fSession);
  4513.             fSemIntf->RegisterRealPart(ev, this);        
  4514.         }
  4515.  
  4516.         ODAcquireObject(ev, fSemIntf);    
  4517.         return fSemIntf;
  4518.     } 
  4519.     else
  4520.     {
  4521.         return kODNULL;    
  4522.     }
  4523. }
  4524.  
  4525. //------------------------------------------------------------------------------
  4526. // Method:        ReleaseExtension
  4527. // Origin:        ODPart
  4528. //
  4529. // Description:    The method is called when the last instance of an extension
  4530. //                belonging to this part has been released.
  4531. //------------------------------------------------------------------------------
  4532.  
  4533. ODBoolean SoundEditor::ReleaseExtension( Environment*    ev,
  4534.                                          ODExtension*     extension )
  4535. {
  4536.     SOM_Trace("SoundEditor","ReleaseExtension");
  4537.  
  4538.     if ( ODObjectsAreEqual(ev, extension, fSettingsExtension) )
  4539.     {
  4540.         ODDeleteObject(fSettingsExtension);
  4541.         return kODTrue;
  4542.     }
  4543.     else if ( ODObjectsAreEqual(ev, extension, fSemIntf) )
  4544.     {
  4545.         ODDeleteObject(fSemIntf);
  4546.         return kODTrue;
  4547.     }
  4548.     else
  4549.     {
  4550.         return kODFalse;
  4551.     }
  4552. }
  4553.  
  4554. //==============================================================================
  4555. #pragma mark    • SoundEditor-specific •
  4556. //==============================================================================
  4557.  
  4558. //------------------------------------------------------------------------------
  4559. // Method:        ShowSettingsDialog
  4560. // Origin:        SoundEditor
  4561. //
  4562. // Description:    The method is called by the part when the user chooses the
  4563. //                Settings menu item or clicks the Settings button in Part Info.
  4564. //------------------------------------------------------------------------------
  4565.  
  4566. void SoundEditor::ShowSettingsDialog( Environment*    ev,
  4567.                                       ODFrame*        frame )
  4568. {
  4569.     SOM_Trace("SoundEditor","ShowSettingsDialog");
  4570.  
  4571.     TempFocus modalFocus(ev, gGlobals->fModalFocus, frame);
  4572.     if ( modalFocus.Request() )
  4573.     {
  4574.         // Dim the frontmost document window.
  4575.         fSession->GetWindowState(ev)->DeactivateFrontWindows(ev);
  4576.             
  4577.         DialogPtr dialog = kODNULL;
  4578.         ODVolatile(dialog);
  4579.  
  4580.         TRY
  4581.             CUsingLibraryResources res;
  4582.             
  4583.             dialog = GetNewDialog(kSettingsDialogID, kODNULL, (WindowPtr) -1L);
  4584.             THROW_IF_NULL(dialog);
  4585.     
  4586.             SetDialogDefaults(dialog, kDialogFontInfoID, 1);
  4587.  
  4588.             short            itemType;
  4589.             Rect            itemRect;
  4590.             ControlHandle    goodButton;
  4591.             ControlHandle    betterButton;
  4592.             ControlHandle    bestButton;
  4593.             
  4594.             GetDialogItem(dialog, kGood,   &itemType, (Handle*) &goodButton,   &itemRect);
  4595.             GetDialogItem(dialog, kBetter, &itemType, (Handle*) &betterButton, &itemRect);
  4596.             GetDialogItem(dialog, kBest,   &itemType, (Handle*) &bestButton,   &itemRect);
  4597.  
  4598.             SetControlValue(goodButton,   (fRecordingQuality == siGoodQuality));
  4599.             SetControlValue(betterButton, (fRecordingQuality == siBetterQuality));
  4600.             SetControlValue(bestButton,   (fRecordingQuality == siBestQuality));
  4601.             
  4602.             SetCursor(&ODQDGlobals.arrow);
  4603.             ShowWindow(dialog);
  4604.     
  4605.             short itemHit;
  4606.             do
  4607.             {
  4608.                 ModalDialog(kODNULL, &itemHit);
  4609.             
  4610.                 switch ( itemHit )
  4611.                 {
  4612.                     case kGood:
  4613.                     case kBetter:
  4614.                     case kBest:
  4615.                     {
  4616.                         SetControlValue(goodButton,   (itemHit == kGood));
  4617.                         SetControlValue(betterButton, (itemHit == kBetter));
  4618.                         SetControlValue(bestButton,   (itemHit == kBest));
  4619.                     }
  4620.                     break;
  4621.                 }
  4622.             }
  4623.             while ( itemHit != ok && itemHit != cancel );            
  4624.     
  4625.             if ( itemHit == ok )
  4626.             {
  4627.                 if ( GetControlValue(goodButton) )
  4628.                     this->SetRecordingQuality(siGoodQuality);
  4629.                 else if ( GetControlValue(betterButton) )
  4630.                     this->SetRecordingQuality(siBetterQuality);
  4631.                 else if ( GetControlValue(bestButton) )
  4632.                     this->SetRecordingQuality(siBestQuality);
  4633.             }
  4634.     
  4635.         CATCH_ALL
  4636.             if (dialog)
  4637.                 DisposeDialog(dialog);
  4638.  
  4639.             // Hilite the frontmost document window.
  4640.             fSession->GetWindowState(ev)->ActivateFrontWindows(ev);
  4641.             
  4642.             RERAISE;
  4643.         ENDTRY
  4644.         
  4645.         DisposeDialog(dialog);
  4646.  
  4647.         // Hilite the frontmost document window.
  4648.         fSession->GetWindowState(ev)->ActivateFrontWindows(ev);
  4649.     }
  4650. }
  4651.  
  4652. //------------------------------------------------------------------------------
  4653.  
  4654. #pragma segment SoundEditorContentHandling
  4655.  
  4656. //------------------------------------------------------------------------------
  4657. // Method:        PlaySound
  4658. // Origin:        SoundEditor
  4659. //
  4660. // Description:    The method is called by the part when the Play button is enabled
  4661. //                and the user has clicked on it.
  4662. //------------------------------------------------------------------------------
  4663.  
  4664. void SoundEditor::PlaySound( Environment*    ev)
  4665. {
  4666.     SOM_Trace("SoundEditor","PlaySound");
  4667.     
  4668.     // If there is no sound to play, then return immediately
  4669.     if ( fPlayer->GetSound() == kODNULL )
  4670.         return;
  4671.  
  4672.     ODFrame* frame = this->FindFirstAvailableFrame(ev);
  4673.  
  4674.     // Request sound out focus
  4675.     if ( fSession->GetArbitrator(ev)
  4676.                 ->RequestFocus(ev, gGlobals->fSoundOutFocus, frame) )
  4677.     {
  4678.         this->FocusAcquired(ev, gGlobals->fSoundOutFocus, frame);
  4679.     }
  4680.     else
  4681.     {
  4682.         this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrEditorPlaying);
  4683.         return;
  4684.     }
  4685.         
  4686.     if ( !fPlayer->IsInitialized() )
  4687.     {
  4688.         this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrSndChannelNotInited);
  4689.         return;
  4690.     }
  4691.             
  4692.     TRY
  4693.         // Play the sound.                
  4694.         if ( fPlayer->Start(frame) )
  4695.         {    
  4696.             fSession->GetDispatcher(ev)->
  4697.                     RegisterIdle(ev, fSelf, frame, kSoundEditorIdle);
  4698.  
  4699.             this->InvalidateButton(ev, kRecordButton);
  4700.             this->InvalidateButton(ev, kStopButton);
  4701.             this->InvalidateButton(ev, kPauseButton);
  4702.             this->InvalidateButton(ev, kPlayButton);
  4703.             
  4704.             this->InvalidateFrameViewRect(ev, gGlobals->fCurrentTimeRect);
  4705.             this->InvalidateFrameViewRect(ev, gGlobals->fSpeakerIconRect);
  4706.         }
  4707.         else
  4708.         {
  4709.             this->DoDialogBox(ev, frame, kErrorBoxID, kErrCannotPlaySound);
  4710.         }
  4711.             
  4712.     CATCH_ALL
  4713.         this->DoDialogBox(ev, frame, kErrorBoxID, kErrCannotPlaySound);
  4714.     ENDTRY
  4715. }
  4716.  
  4717. //------------------------------------------------------------------------------
  4718. // Method:        StopPlayingSound
  4719. // Origin:        SoundEditor
  4720. //
  4721. // Description:    The method is called by the part when...
  4722. //------------------------------------------------------------------------------
  4723.  
  4724. void SoundEditor::StopPlayingSound( Environment*    ev )
  4725. {
  4726.     SOM_Trace("SoundEditor","StopPlayingSound");
  4727.                                 
  4728.     if ( !fPlayer->IsInitialized() )
  4729.     {
  4730.         this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrSndChannelNotInited);
  4731.         return;
  4732.     }
  4733.  
  4734.     ODBoolean wasPaused = fPlayer->IsPaused();
  4735.  
  4736.     if ( fPlayer->Stop() )
  4737.     {
  4738.         ODFrame* frame = this->FindFirstAvailableFrame(ev);
  4739.         
  4740.         fSession->GetArbitrator(ev)
  4741.                 ->RelinquishFocus(ev, gGlobals->fSoundOutFocus, frame);            
  4742.         this->FocusLost(ev, gGlobals->fSoundOutFocus, frame);
  4743.     
  4744.         // Stop checking the status of the sound channel.
  4745.         fSession->GetDispatcher(ev)->
  4746.                 UnregisterIdle(ev, fSelf, frame);
  4747.  
  4748.         // Invalidate the play button to pop it back up
  4749.         this->InvalidateButton(ev, kRecordButton);
  4750.         this->InvalidateButton(ev, kStopButton);
  4751.         this->InvalidateButton(ev, kPauseButton);
  4752.         this->InvalidateButton(ev, kPlayButton);
  4753.  
  4754.         this->InvalidateFrameViewRect(ev, gGlobals->fSpeakerIconRect);
  4755.         this->InvalidateFrameViewRect(ev, gGlobals->fProgressBarRect);
  4756.         this->InvalidateFrameViewRect(ev, gGlobals->fCurrentTimeRect);
  4757.     }
  4758. }
  4759.  
  4760. //------------------------------------------------------------------------------
  4761. // Method:        PausePlayingSound
  4762. // Origin:        SoundEditor
  4763. //
  4764. // Description:    The method is called by the part when...
  4765. //------------------------------------------------------------------------------
  4766.  
  4767. void SoundEditor::PausePlayingSound(     Environment*    ev )
  4768. {
  4769.     SOM_Trace("SoundEditor","PausePlayingSound");
  4770.     
  4771.     if ( !fPlayer->IsInitialized() )
  4772.     {
  4773.         this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrSndChannelNotInited);
  4774.         return;
  4775.     }
  4776.  
  4777.     if ( fPlayer->Pause() )
  4778.     {
  4779.         this->InvalidateButton(ev, kPauseButton);
  4780.         
  4781.         this->InvalidateFrameViewRect(ev, gGlobals->fSpeakerIconRect);
  4782.     }
  4783. }
  4784.  
  4785. //------------------------------------------------------------------------------
  4786. // Method:        ResumePlayingSound
  4787. // Origin:        SoundEditor
  4788. //
  4789. // Description:    The method is called by the part when...
  4790. //------------------------------------------------------------------------------
  4791.  
  4792. void SoundEditor::ResumePlayingSound(     Environment*    ev )
  4793. {
  4794.     SOM_Trace("SoundEditor","ResumePlayingSound");
  4795.     
  4796.     if ( !fPlayer->IsInitialized() )
  4797.     {
  4798.         this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrSndChannelNotInited);
  4799.         return;
  4800.     }
  4801.  
  4802.     if ( fPlayer->Resume() )
  4803.     {
  4804.         this->InvalidateButton(ev, kPauseButton);
  4805.         
  4806.         this->InvalidateFrameViewRect(ev, gGlobals->fSpeakerIconRect);
  4807.     }
  4808. }
  4809.  
  4810. //------------------------------------------------------------------------------
  4811. // Method:        UpdateDisplay
  4812. // Origin:        SoundEditor
  4813. //
  4814. // Description:    The method is called by the part while a sound is recording or
  4815. //                playing to update the progress bar, time indicator, and speaker
  4816. //                icon.
  4817. //
  4818. //                This routine returns a boolean value indicating if the sounder
  4819. //                is completed.  This must be done because of the implementation
  4820. //                of the sounder class, where IsCompleted is called to update
  4821. //                the time field.
  4822. //------------------------------------------------------------------------------
  4823.  
  4824. ODBoolean SoundEditor::UpdateDisplay( Environment* ev, CSounder* sounder )
  4825. {
  4826.     SOM_Trace("SoundEditor","UpdateDisplay");
  4827.  
  4828.     // If we don't need to do any drawing, just return.
  4829.     if ( sounder == fPlayer && sounder->IsPaused() )
  4830.         return kODFalse;
  4831.  
  4832.     ODULong oldTime = sounder->GetTime();
  4833.         // We have to get the old time before calling IsCompleted because
  4834.         // IsCompleted will update the time.
  4835.  
  4836.     ODBoolean done = sounder->IsCompleted();
  4837.     
  4838.     // We have to actually draw the progress bar, time, and meter here.
  4839.     // We can't just invalidate them because it will cause an awful flicker.
  4840.  
  4841.     fCurrentTime = sounder->GetTime();
  4842.     
  4843.     if (sounder == fPlayer)
  4844.     {
  4845.         if ( fUnknownSound )                // If sound has unknown length
  4846.             fSecsRecorded = -fCurrentTime;    // use current value (as -)
  4847.         if ( fCurrentTime > fSecsRecorded )
  4848.             fCurrentTime = fSecsRecorded;
  4849.     }
  4850.     else
  4851.     {
  4852.         if ( fCurrentTime > fMaxSeconds )
  4853.             fCurrentTime = fMaxSeconds;
  4854.     }
  4855.     
  4856.     ODSShort progBar;
  4857.     if ( fMaxSeconds )
  4858.         progBar = (fCurrentTime * 100) / fMaxSeconds;
  4859.     else
  4860.         progBar = 0;
  4861.     
  4862.     // Iterate over the frames we are displayed through and determine which
  4863.     // ones we need to invalidate.
  4864.     CListIterator fiter(fDisplayFrames);
  4865.     for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
  4866.             fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
  4867.     {
  4868.         // If the display frame is real (has been "connected" or was "added")
  4869.         // get its view type; otherwise, ignore it.
  4870.         ODFrame* frame = proxy->GetFrame(ev);
  4871.         if ( frame != kODNULL
  4872.              && frame->GetPresentation(ev) == gGlobals->fControlsPresentation
  4873.              && frame->GetViewType(ev) == gGlobals->fFrameView )
  4874.         {
  4875.             for ( TempODFrameFacetIterator facet(ev, frame); 
  4876.                   facet.IsNotComplete(); facet.Next() )
  4877.             {
  4878.                 if ( !sounder->IsPaused() )
  4879.                 {
  4880.                     this->DrawCurrentTime(ev, facet, oldTime, fCurrentTime);
  4881.                     this->DrawProgressBar(ev, facet, progBar, 
  4882.                                           (sounder == fPlayer) ? kPlayingPAT : kRecordOnPAT);
  4883.                 }
  4884.                 
  4885.                 CFocus initiateDrawing(ev, facet);
  4886.                 DrawEditorPicture(kSpeaker0PICT + sounder->GetLevel(),
  4887.                                   gGlobals->fSpeakerIconRect);                                
  4888.             }
  4889.         }
  4890.     }
  4891.     
  4892.     return done;
  4893. }
  4894.  
  4895. //------------------------------------------------------------------------------
  4896. // Method:        SoundDonePlaying
  4897. // Origin:        SoundEditor
  4898. //
  4899. // Description:    The method is called by the part when...
  4900. //------------------------------------------------------------------------------
  4901.  
  4902. ODBoolean SoundEditor::SoundDonePlaying(     Environment*    ev )
  4903. {
  4904.     SOM_Trace("SoundEditor","SoundDonePlaying");
  4905.  
  4906.     if ( !this->SoundPlaying() )
  4907.         return kODFalse;
  4908.         
  4909.     return this->UpdateDisplay(ev, fPlayer);
  4910. }
  4911.  
  4912. //------------------------------------------------------------------------------
  4913. // Method:        RecordSound
  4914. // Origin:        SoundEditor
  4915. //
  4916. // Description:    The method is called by the part when it wants to start
  4917. //                recording a sound.
  4918. //------------------------------------------------------------------------------
  4919.  
  4920. void SoundEditor::RecordSound( Environment*        ev )
  4921. {
  4922.     SOM_Trace("SoundEditor","RecordSound");
  4923.     
  4924.     ODFrame* frame = this->FindFirstAvailableFrame(ev);
  4925.  
  4926.     TRY
  4927.         // Record the sound.        
  4928.         
  4929.         // Request sound in focus
  4930.         if ( fSession->GetArbitrator(ev)
  4931.                     ->RequestFocus(ev, gGlobals->fSoundInFocus, frame) )
  4932.         {        
  4933.             this->FocusAcquired(ev, gGlobals->fSoundInFocus, frame);
  4934.     
  4935.             // If the user is interupting the current sound being played,
  4936.             // then stop the sound before recording.
  4937.             if ( this->SoundPlaying() )
  4938.                 this->StopPlayingSound(ev);
  4939.     
  4940.             this->InvalidateButton(ev, kRecordButton);
  4941.     
  4942.             Size grow;
  4943.             Size availableMemory = TempMaxMem(&grow);
  4944.             
  4945.             if ( availableMemory < 64 * 1024 )
  4946.             {
  4947.                 this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrOutOfMemory); 
  4948.                 return;
  4949.             }
  4950.             
  4951.             // Make sure there is at least one 32k contiguous block of memory.
  4952.             // This helps prevent spurious "out of memory" errors in the toolbox.
  4953.             if ( availableMemory >= (32768 + fMaxMemory) )
  4954.                 availableMemory = fMaxMemory;
  4955.             else
  4956.                 availableMemory -= 32768;
  4957.             
  4958.             // Using OpenDoc's memory mgr, allocate a buffer in temp memory.
  4959.             ODHandle tempHandle = ODNewHandle(availableMemory);
  4960.             fRecorder->SetSound(tempHandle);
  4961.     
  4962.             // If an asynchronous sound device can be opened, do asynchronous recording
  4963.             fRecorder->SetQuality(fRecordingQuality);
  4964.             if ( fRecorder->Start(frame) )
  4965.             {
  4966.                 fMaxSeconds = fRecorder->GetMaxTime(availableMemory, fRecordingQuality);
  4967.     
  4968.                 fSession->GetDispatcher(ev)->
  4969.                         RegisterIdle(ev, fSelf, frame, kSoundEditorIdle);
  4970.     
  4971.                 // no need to invalidate buttons - "record on" button already drawn above
  4972.                 this->InvalidateButton(ev, kStopButton);
  4973.                 this->InvalidateButton(ev, kPauseButton);
  4974.                 this->InvalidateButton(ev, kPlayButton);
  4975.     
  4976.                 // do need to update the max time and the progress bar to record though
  4977.                 this->InvalidateFrameViewRect(ev, gGlobals->fCurrentTimeRect);
  4978.                 this->InvalidateFrameViewRect(ev, gGlobals->fMaxTimeRect);
  4979.                 this->InvalidateFrameViewRect(ev, gGlobals->fProgressBarRect);
  4980.             }
  4981.             else
  4982.             {
  4983.                 // unable to open asynchronous sound device use the modal window instead
  4984.                 TempFocus modalFocus(ev, gGlobals->fModalFocus, frame);
  4985.                 TempFocus soundInFocus(ev, gGlobals->fModalFocus, frame);
  4986.                 
  4987.                 if ( modalFocus.Request() && soundInFocus.Request() )
  4988.                 {    
  4989.                     Rect scratchRect;
  4990.                     SetRect(&scratchRect, 0, 0, 10, 10);
  4991.                     this->CalcPartWindowPosition(ev, frame, &scratchRect);
  4992.                     
  4993.                     Point topLeft;
  4994.                     topLeft.v = scratchRect.top;
  4995.                     topLeft.h = scratchRect.left;
  4996.             
  4997.                     // Dim the frontmost document window.
  4998.                     fSession->GetWindowState(ev)->DeactivateFrontWindows(ev);
  4999.                     
  5000.                     // Record sound using Standard Record dialog provided by the Sound Mgr.
  5001.                     OSErr error = SndRecord(kODNULL, topLeft, fRecordingQuality,
  5002.                                             (SndListHandle*) &tempHandle);
  5003.                     
  5004.                     // Hilite the frontmost document window.
  5005.                     fSession->GetWindowState(ev)->ActivateFrontWindows(ev);
  5006.                     
  5007.                     // Invalidate the record button to pop it back up
  5008.                     this->InvalidateButton(ev, kRecordButton);
  5009.                     this->InvalidateButton(ev, kStopButton);
  5010.                     this->InvalidateButton(ev, kPauseButton);
  5011.                     this->InvalidateButton(ev, kPlayButton);
  5012.                     
  5013.                     // If all went well, save the newly recorded sound.
  5014.                     if ( error != userCanceledErr )
  5015.                     {
  5016.                         THROW_IF_ERROR(error);
  5017.                         
  5018.                         fPlayer->SetSound(tempHandle);
  5019.                         if (fPreferredKind == kODNullTypeToken)
  5020.                             fPreferredKind = gGlobals->fNativeData;
  5021.                         
  5022.                         this->SetDirty(ev);
  5023.                     }
  5024.                 }
  5025.                 else    // unable to acquire Modal focus
  5026.                 {
  5027.                     this->DoDialogBox(ev, frame, kErrorBoxID, kErrEditorModal);
  5028.                 }
  5029.             }
  5030.         }
  5031.         else            // unable to acquire SoundIn focus
  5032.         {
  5033.             this->DoDialogBox(ev, frame, kErrorBoxID, kErrEditorRecording);
  5034.         }
  5035.     
  5036.     CATCH_ALL
  5037.         this->DoDialogBox(ev, frame, kErrorBoxID, kErrCannotRecordSound);
  5038.     ENDTRY
  5039. }
  5040.  
  5041. //------------------------------------------------------------------------------
  5042. // Method:        StopRecordingSound
  5043. // Origin:        SoundEditor
  5044. //
  5045. // Description:    The method is called by the part when...
  5046. //------------------------------------------------------------------------------
  5047.  
  5048. void SoundEditor::StopRecordingSound( Environment*    ev )
  5049. {
  5050.     SOM_Trace("SoundEditor","StopRecordingSound");
  5051.     
  5052.     if ( !fRecorder->IsInitialized() )
  5053.     {
  5054.         this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrSndDeviceNotOpened);
  5055.         return;
  5056.     }
  5057.  
  5058.     ODBoolean wasPaused = fRecorder->IsPaused();
  5059.  
  5060.     if ( fRecorder->Stop() )
  5061.     {
  5062.         ODFrame* frame = this->FindFirstAvailableFrame(ev);
  5063.         
  5064.         // Stop checking the status of the sound input device.
  5065.          
  5066.         fSession->GetDispatcher(ev)->UnregisterIdle(ev, fSelf, frame);
  5067.  
  5068.         // Save the recorded sound into the player object
  5069.         fPlayer->SetSound(fRecorder->GetSound());
  5070.                                 
  5071.         fSecsRecorded = fRecorder->GetTime();
  5072.         fUnknownSound = kODFalse;
  5073.         
  5074.         if (fPreferredKind == kODNullTypeToken)
  5075.             fPreferredKind = gGlobals->fNativeData;
  5076.         
  5077.         this->SetDirty(ev);
  5078.         
  5079.         fSession->GetArbitrator(ev)
  5080.                 ->RelinquishFocus(ev, gGlobals->fSoundInFocus, frame);            
  5081.         this->FocusLost(ev, gGlobals->fSoundInFocus, frame);
  5082.  
  5083.         this->InvalidateButton(ev, kRecordButton);
  5084.         this->InvalidateButton(ev, kStopButton);
  5085.         this->InvalidateButton(ev, kPauseButton);
  5086.         this->InvalidateButton(ev, kPlayButton);
  5087.  
  5088.         this->InvalidateFrameViewRect(ev, gGlobals->fSpeakerIconRect);
  5089.         this->InvalidateFrameViewRect(ev, gGlobals->fProgressBarRect);
  5090.     }
  5091. }
  5092.  
  5093. //------------------------------------------------------------------------------
  5094. // Method:        PauseRecordingSound
  5095. // Origin:        SoundEditor
  5096. //
  5097. // Description:    The method is called by the part when...
  5098. //------------------------------------------------------------------------------
  5099.  
  5100. void SoundEditor::PauseRecordingSound( Environment*    ev )
  5101. {
  5102.     SOM_Trace("SoundEditor","PauseRecordingSound");
  5103.     
  5104.     if ( !fRecorder->IsInitialized() )
  5105.     {
  5106.         this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrSndChannelNotInited);
  5107.         return;
  5108.     }
  5109.             
  5110.     if ( fRecorder->Pause() )
  5111.         this->InvalidateButton(ev, kPauseButton);
  5112. }
  5113.  
  5114. //------------------------------------------------------------------------------
  5115. // Method:        ResumeRecordingSound
  5116. // Origin:        SoundEditor
  5117. //
  5118. // Description:    The method is called by the part when...
  5119. //------------------------------------------------------------------------------
  5120.  
  5121. void SoundEditor::ResumeRecordingSound( Environment* ev )
  5122. {
  5123.     SOM_Trace("SoundEditor","ResumeRecordingSound");
  5124.     
  5125.     if ( !fRecorder->IsInitialized() )
  5126.     {
  5127.         this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrSndChannelNotInited);
  5128.         return;
  5129.     }
  5130.  
  5131.     if ( fRecorder->Resume() )
  5132.         this->InvalidateButton(ev, kPauseButton);
  5133. }
  5134.  
  5135. //------------------------------------------------------------------------------
  5136. // Method:        SoundDoneRecording
  5137. // Origin:        SoundEditor
  5138. //
  5139. // Description:    The method is called by the part when...
  5140. //------------------------------------------------------------------------------
  5141.  
  5142. ODBoolean SoundEditor::SoundDoneRecording( Environment*    ev )
  5143. {
  5144.     SOM_Trace("SoundEditor","SoundDoneRecording");
  5145.  
  5146.     if ( !this->SoundRecording() )
  5147.         return kODFalse;
  5148.     
  5149.     return this->UpdateDisplay(ev, fRecorder);
  5150. }
  5151.  
  5152. //------------------------------------------------------------------------------
  5153. // Methods:        Methods to find out state of editor, and certain 
  5154. //                information about it
  5155. // Origin:        SoundEditor
  5156. //
  5157. // Description:    The method is called by the part when...
  5158. //------------------------------------------------------------------------------
  5159.  
  5160. ODBoolean SoundEditor::SoundPlaying()
  5161. {
  5162.     return fPlayer->IsPlaying();
  5163. }    
  5164.  
  5165. ODBoolean SoundEditor::SoundRecording()
  5166. {
  5167.     return fRecorder->IsRecording();
  5168. }    
  5169.  
  5170. ODBoolean SoundEditor::SoundPaused()
  5171. {
  5172.     return fPlayer->IsPaused() || fRecorder->IsPaused();
  5173. }    
  5174.  
  5175. OSType SoundEditor::GetRecordingQuality()
  5176. {
  5177.     return fRecordingQuality;
  5178. }
  5179.  
  5180. void SoundEditor::SetRecordingQuality(OSType rQuality)
  5181. {
  5182.     if ( rQuality != fRecordingQuality ) 
  5183.     {
  5184.         Environment* ev = somGetGlobalEnvironment();
  5185.  
  5186.         fRecordingQuality = rQuality;
  5187.         fMaxSeconds = fRecorder->GetMaxTime(fMaxMemory, fRecordingQuality);
  5188.     
  5189.         this->InvalidateFrameViewRect(ev, gGlobals->fMaxTimeRect);
  5190.         this->InvalidateFrameViewRect(ev, gGlobals->fProgressBarRect);
  5191.         this->SetDirty(ev);
  5192.     }
  5193. }
  5194.  
  5195. ODBoolean SoundEditor::GetSoundQuality(OSType* rQuality)
  5196. {
  5197.     if (fUnknownSound)
  5198.         return kODFalse;
  5199.         
  5200.     *rQuality = fRecorder->GetQuality();
  5201.     return kODTrue;
  5202. }
  5203.  
  5204. ODULong SoundEditor::GetCurrentTime()
  5205. {
  5206.     return fCurrentTime;
  5207. }
  5208.  
  5209. ODULong SoundEditor::GetMaxTime()
  5210. {
  5211.     return fMaxSeconds;
  5212. }
  5213.  
  5214. ODULong SoundEditor::GetRecordedTime()
  5215. {
  5216.     return fSecsRecorded;
  5217. }
  5218.  
  5219.  
  5220. ODBoolean SoundEditor::IsPlayAvailable()
  5221. {
  5222.     return ( fPlayer->HasSound()
  5223.              && (!fPlayer->IsPlaying() || fPlayer->IsPaused()) 
  5224.              && !fRecorder->IsRecording() );
  5225. }
  5226.                                                     
  5227. ODBoolean SoundEditor::IsPauseAvailable()
  5228. {
  5229.     return ( fPlayer->IsPlaying() || fRecorder->IsRecording() );
  5230. }
  5231.  
  5232. ODBoolean SoundEditor::IsStopAvailable()
  5233. {
  5234.     return ( fPlayer->IsPlaying() || fRecorder->IsRecording() );
  5235. }
  5236.  
  5237. ODBoolean SoundEditor::IsRecordAvailable()
  5238. {
  5239.     return ( (!fRecorder->IsRecording() || fRecorder->IsPaused())
  5240.              && !fReadOnlyStorage );
  5241. }    
  5242.  
  5243.  
  5244. //------------------------------------------------------------------------------
  5245. // Method:        DrawCurrentTime
  5246. // Origin:        SoundEditor
  5247. //
  5248. // Description:    The method is called by the part when...
  5249. //------------------------------------------------------------------------------
  5250.  
  5251. void SoundEditor::DrawCurrentTime( Environment*    ev,
  5252.                                     ODFacet*        facet,
  5253.                                     ODSShort        oldTime,
  5254.                                     ODSShort        curTime )
  5255. {
  5256.     SOM_Trace("SoundEditor","DrawCurrentTime");
  5257.  
  5258.     if ( curTime < 0 )
  5259.         curTime = 0;
  5260.         
  5261.     if ( oldTime > -1 && oldTime == curTime )
  5262.         return;
  5263.     
  5264.     // Draw the current time
  5265.     DrawTime(ev, facet, gGlobals->fCurrentTimeRect, 12, curTime);
  5266. }
  5267.  
  5268. //------------------------------------------------------------------------------
  5269. // Method:        DrawProgressBar
  5270. // Origin:        SoundEditor
  5271. //
  5272. // Description:    The method is called by the part when...
  5273. //------------------------------------------------------------------------------
  5274.  
  5275. void SoundEditor::DrawProgressBar(    Environment*     ev, 
  5276.                                     ODFacet*         facet,
  5277.                                     ODSShort         progress, 
  5278.                                     ODSShort         pixPatID )
  5279. {
  5280.     SOM_Trace("SoundEditor","DrawProgressBar");
  5281.  
  5282.     if ( progress > 100 )
  5283.         progress = 100;
  5284.     if ( progress < 0 )
  5285.         progress = 0;
  5286.         
  5287.     ODSShort sizeBar = (gGlobals->fProgressBarRect.right - gGlobals->fProgressBarRect.left) 
  5288.                             * progress / 100;
  5289.     
  5290.     Rect newBar  = gGlobals->fProgressBarRect;
  5291.     newBar.right = newBar.left + sizeBar;
  5292.         
  5293.     {
  5294.         CUsingLibraryResources res;
  5295.         CFocus initiateDrawing(ev, facet);
  5296.         
  5297.         PixPatHandle thePat = GetPixPat(pixPatID);
  5298.         FillCRect(&newBar, thePat);
  5299.         DisposePixPat(thePat);
  5300.     }
  5301. }
  5302.  
  5303. //------------------------------------------------------------------------------
  5304. // Method:        CopySound
  5305. // Origin:        SoundEditor
  5306. //
  5307. // Description:    The method is called by the part when...
  5308. //------------------------------------------------------------------------------
  5309.  
  5310. void SoundEditor::CopySound( Environment*    ev,
  5311.                              ODFrame*        frame )
  5312. {
  5313.     SOM_Trace("SoundEditor","CopySound");
  5314.  
  5315.     // Acquire clipboard focus
  5316.     TempClipboardFocus clipboardFocus(ev, frame);
  5317.     if ( clipboardFocus.Request() )
  5318.     {
  5319.         ODClipboard* clipboard = fSession->GetClipboard(ev);
  5320.         clipboard->Clear(ev);
  5321.         
  5322.         ODStorageUnit*    clipboardSU = clipboard->GetContentStorageUnit(ev);
  5323.         ODDraft*        fromDraft = ODGetDraft(ev, fSelf);
  5324.         ODDraft*        toDraft = clipboardSU->GetDraft(ev);
  5325.         ODDraftKey        key = kODNULLKey;
  5326.         
  5327.         ODVolatile(key);
  5328.     
  5329.         TRY
  5330.             key = fromDraft->BeginClone(ev, toDraft, kODNULL, kODCloneCopy);
  5331.         
  5332.             // Verify that the properties we want to write into exist.
  5333.             this->CheckAndAddProperties(ev, clipboardSU);
  5334.                 
  5335.             // Write out the part's content annotation.
  5336.             this->ExternalizeAnnotation(ev, clipboardSU, key, frame);
  5337.  
  5338.             // Write out the part's content.
  5339.             if ( fPreferredKind == gGlobals->fNativeData )
  5340.             {
  5341.                 this->ExternalizeContent(ev, clipboardSU, key, frame, gGlobals->fNativeData);
  5342.                 this->ExternalizeContent(ev, clipboardSU, key, frame, gGlobals->fMacSndData);
  5343.             }
  5344.             else if ( fPreferredKind == gGlobals->fMacSndData )
  5345.             {
  5346.                 this->ExternalizeContent(ev, clipboardSU, key, frame, gGlobals->fMacSndData);
  5347.             }
  5348.             else
  5349.             {
  5350.                 WARN("fPreferredKind is an unknown kind.");
  5351.             }
  5352.             
  5353.             fromDraft->EndClone(ev, key);
  5354.         CATCH_ALL
  5355.             if ( key != kODNULLKey )
  5356.                 fromDraft->AbortClone(ev, key);
  5357.             
  5358.             clipboard->Clear(ev);
  5359.             RERAISE;
  5360.         ENDTRY
  5361.     }
  5362.     else
  5363.     {
  5364.         this->DoDialogBox(ev, frame, kErrorBoxID, kErrCantGetClipboard);
  5365.     }
  5366. }
  5367.  
  5368. //------------------------------------------------------------------------------
  5369. // Method:        AnnotateClipboard
  5370. // Origin:        SoundEditor
  5371. //
  5372. // Description:    This method is called by the part...
  5373. //------------------------------------------------------------------------------
  5374.  
  5375. void SoundEditor::AnnotateClipboard( Environment*    ev,
  5376.                                      ODFrame*        frame )
  5377. {
  5378.     SOM_Trace("SoundEditor","AnnotateClipboard");
  5379.  
  5380.     // Acquire clipboard focus
  5381.     TempClipboardFocus clipboardFocus(ev, frame);
  5382.     if ( clipboardFocus.Request() )
  5383.     {
  5384.         ODClipboard* clipboard = fSession->GetClipboard(ev);
  5385.         ODStorageUnit* clipboardSU = clipboard->GetContentStorageUnit(ev);
  5386.         clipboardSU->AddProperty(ev, kODPropSuggestedFrameShape);
  5387.         
  5388.         TempODShape frameShape = frame->AcquireFrameShape(ev, kODNULL);
  5389.         frameShape->WriteShape(ev, clipboardSU);
  5390.     }
  5391. }
  5392.  
  5393. //------------------------------------------------------------------------------
  5394. // Method:        PasteSound
  5395. // Origin:        SoundEditor
  5396. //
  5397. // Description:    The method is called by the part when...
  5398. //------------------------------------------------------------------------------
  5399.  
  5400. void SoundEditor::PasteSound( Environment*    ev,
  5401.                               ODFrame*        frame )
  5402. {
  5403.     SOM_Trace("SoundEditor","PasteSound");
  5404.  
  5405.     // Acquire clipboard focus
  5406.     TempClipboardFocus clipboardFocus(ev, frame);
  5407.     if ( clipboardFocus.Request() )
  5408.     {
  5409.         ODClipboard*    clipboard   = fSession->GetClipboard(ev);
  5410.         ODStorageUnit*    clipboardSU = clipboard->GetContentStorageUnit(ev);
  5411.     
  5412.         // Read in the content annotation.
  5413.         this->InternalizeAnnotation(ev, clipboardSU);
  5414.  
  5415.         // Read in the new sound data.
  5416.         this->InternalizeContent(ev, clipboardSU);
  5417.     
  5418.         this->SetDirty(ev);
  5419.  
  5420.         this->InvalidateButton(ev, kPlayButton);
  5421.         
  5422.         // Update Window to reflect changes in contained sound
  5423.         this->InvalidateFrameViewRect(ev, gGlobals->fCurrentTimeRect);
  5424.         this->InvalidateFrameViewRect(ev, gGlobals->fMaxTimeRect);
  5425.         this->InvalidateFrameViewRect(ev, gGlobals->fProgressBarRect);
  5426.     }
  5427.     else
  5428.     {
  5429.         this->DoDialogBox(ev, frame, kErrorBoxID, kErrCantGetClipboard);
  5430.     }
  5431. }
  5432.  
  5433. //------------------------------------------------------------------------------
  5434. // Method:        ClearSound
  5435. // Origin:        SoundEditor
  5436. //
  5437. // Description:    The method is called by the part when...
  5438. //------------------------------------------------------------------------------
  5439.  
  5440. void SoundEditor::ClearSound(     Environment*    ev,
  5441.                                  ODFrame*        frame )
  5442.  
  5443. {
  5444.     SOM_Trace("SoundEditor","ClearSound");
  5445.  
  5446.     // Discard sound seconds recorded
  5447.     fPlayer->DisposeSound();
  5448.     fSecsRecorded = 0;
  5449.     
  5450.     this->InvalidateButton(ev, kPlayButton);
  5451.  
  5452.     // Update Window to reflect changes in contained sound
  5453.     this->InvalidateFrameViewRect(ev, gGlobals->fCurrentTimeRect);
  5454.     this->InvalidateFrameViewRect(ev, gGlobals->fProgressBarRect);
  5455.  
  5456.     this->SetDirty(ev);
  5457. }
  5458.  
  5459. //------------------------------------------------------------------------------
  5460. // Method:        SoundOnClipboard
  5461. // Origin:        SoundEditor
  5462. //
  5463. // Description:    The method is called by the part to deterine if the clipboard
  5464. //                contains a sound.
  5465. //------------------------------------------------------------------------------
  5466.  
  5467. ODBoolean SoundEditor::SoundOnClipboard( Environment*    ev,
  5468.                                          ODFrame*        frame )
  5469. {
  5470.     SOM_Trace("SoundEditor","SoundOnClipboard");
  5471.  
  5472.     ODBoolean result = kODFalse;
  5473.  
  5474.     // Acquire clipboard focus
  5475.     TempClipboardFocus clipboardFocus(ev, frame);
  5476.     if ( clipboardFocus.Request() )
  5477.     {
  5478.         ODClipboard*    clipboard     = fSession->GetClipboard(ev);
  5479.         ODStorageUnit*    clipboardSU = clipboard->GetContentStorageUnit(ev);
  5480.     
  5481.         result  = clipboardSU->Exists(ev, kODPropContents, kSoundEditorKind, 0);
  5482.         result |= clipboardSU->Exists(ev, kODPropContents, gGlobals->fSndDataKind, 0);
  5483.     }
  5484.  
  5485.     return result;
  5486. }
  5487.  
  5488. //------------------------------------------------------------------------------
  5489. // Method:        FindFirstAvailableFrame
  5490. // Origin:        SoundEditor
  5491. //
  5492. // Description:    The method is called by the part when it needs a frame for
  5493. //                some reason (like acquiring a focus), but there is no particular
  5494. //                frame that is associated with the action.
  5495. //------------------------------------------------------------------------------
  5496.  
  5497. ODFrame* SoundEditor::FindFirstAvailableFrame( Environment*    ev )
  5498. {
  5499.     ODFrame* frame = kODNULL;
  5500.  
  5501.     // We need to locate a frame to use for requesting the modal focus. 
  5502.     // Find the first valid frame in our display frames list.
  5503.     CListIterator fiter(fDisplayFrames);
  5504.     for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
  5505.             fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
  5506.     {
  5507.         if ( proxy->FrameIsLoaded(ev) )
  5508.             frame = proxy->GetFrame(ev);
  5509.         if ( frame ) break;
  5510.     }
  5511.     
  5512.     return frame;
  5513. }
  5514.  
  5515. //------------------------------------------------------------------------------
  5516. // Method:        InvalidateFrameViewRect
  5517. // Origin:        SoundEditor
  5518. //
  5519. // Description:    The method is called by the part when an area of the part
  5520. //                (in frame view or for the controls presentation).
  5521. //------------------------------------------------------------------------------
  5522.  
  5523. void SoundEditor::InvalidateFrameViewRect( Environment*    ev,
  5524.                                            Rect&        bounds )
  5525. {
  5526.     ODRect invalidRect;
  5527.     IntToFixedRect(bounds, invalidRect);
  5528.     
  5529.     // Iterate over the frames we are displayed through and determine which
  5530.     // ones we need to invalidate.
  5531.     CListIterator fiter(fDisplayFrames);
  5532.     for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
  5533.             fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
  5534.     {
  5535.         // If the display frame is real (has been "connected" or was "added")
  5536.         // get its view type; otherwise, ignore it.
  5537.         if ( proxy->FrameIsLoaded(ev) )
  5538.         {
  5539.             ODFrame* frame = proxy->GetFrame(ev);
  5540.  
  5541.             if ( frame->GetPresentation(ev) == gGlobals->fControlsPresentation
  5542.                  && frame->GetViewType(ev) == gGlobals->fFrameView )
  5543.             {
  5544.                 TempODShape invalidShape = frame->CreateShape(ev);
  5545.                 invalidShape->SetRectangle(ev, &invalidRect);
  5546.                 
  5547.                 frame->Invalidate(ev, invalidShape, kODNULL);
  5548.             }
  5549.         }
  5550.     }
  5551. }
  5552.  
  5553. //------------------------------------------------------------------------------
  5554. // Method:        DrawButton
  5555. // Origin:        SoundEditor
  5556. //
  5557. // Description:    The method is called by the part when a button needs to be
  5558. //                drawn.
  5559. //------------------------------------------------------------------------------
  5560.  
  5561. void SoundEditor::DrawButton( Environment*    ev,
  5562.                               EButton        button,
  5563.                               EButtonState    buttonState )
  5564. {
  5565.     ODSShort pictID = kNormalButtonPICT;
  5566.     IconTransformType iconTrans = ttNone;
  5567.     
  5568.     switch (buttonState)
  5569.     {
  5570.         case kButtonStatePressed:
  5571.             pictID = kPressedButtonPICT;
  5572.             iconTrans = ttSelected;
  5573.             break;
  5574.         
  5575.         case kButtonStateOn:
  5576.             pictID = kOnButtonPICT;
  5577.             iconTrans = ttNone;
  5578.             break;
  5579.  
  5580.         case kButtonStateDim:
  5581.             pictID = kDimButtonPICT;
  5582.             iconTrans = ttDisabled;
  5583.             break;
  5584.     }
  5585.         
  5586.     DrawEditorPicture(pictID, gGlobals->fButtonRect[button]);
  5587.     
  5588.     Rect iconRect = gGlobals->fButtonRect[button];
  5589.     iconRect.top    += 5;
  5590.     iconRect.left   += 10;
  5591.     iconRect.bottom = iconRect.top  + kODSmallIconSize;
  5592.     iconRect.right  = iconRect.left + kODSmallIconSize;
  5593.     
  5594.     CUsingLibraryResources res;
  5595.     PlotIconID(&iconRect, atNone, iconTrans, kRecordButtonIcon + button);
  5596. }
  5597.  
  5598. //------------------------------------------------------------------------------
  5599. // Method:        TrackButton
  5600. // Origin:        SoundEditor
  5601. //
  5602. // Description:    The method is called by the part when a button needs to be
  5603. //                tracked because there was a mouse-down in it.
  5604. //------------------------------------------------------------------------------
  5605.  
  5606. ODBoolean SoundEditor::TrackButton( Environment*    ev,
  5607.                                     EButton            button,
  5608.                                     Point            where,
  5609.                                     EButtonState    initialState )
  5610. {
  5611.     ODBoolean isInButton = kODTrue;
  5612.     ODBoolean wasInButton = kODFalse;
  5613.     Point curLoc = where;
  5614.     
  5615.     do 
  5616.     {
  5617.         isInButton = PtInRect(curLoc, &gGlobals->fButtonRect[button]);
  5618.         
  5619.         if (isInButton != wasInButton)
  5620.         {
  5621.             this->DrawButton(ev, button, isInButton ? kButtonStatePressed
  5622.                                                     : initialState);
  5623.             wasInButton = isInButton;
  5624.         }
  5625.         
  5626.         GetMouse(&curLoc);
  5627.     }
  5628.     while (StillDown());
  5629.  
  5630.     return isInButton;
  5631. }
  5632.  
  5633. //------------------------------------------------------------------------------
  5634. // Method:        InvalidateButton
  5635. // Origin:        SoundEditor
  5636. //
  5637. // Description:    The method is called by the part when a button needs to be
  5638. //                tracked because there was a mouse-down in it.
  5639. //------------------------------------------------------------------------------
  5640.  
  5641. void SoundEditor::InvalidateButton(    Environment*    ev,
  5642.                                     EButton            button )
  5643. {
  5644.     this->InvalidateFrameViewRect(ev, gGlobals->fButtonRect[button]);
  5645. }
  5646.  
  5647.